Паттерн «Стратегия» определяет семейство алгоритмов, инкапсулирует каждый из них и обеспечивает их взаимозаменяемость. Он позволяет модифицировать алгоритмы независимо от их использования на стороне клиента (это определение взято из книги “Head First. Паттерны проектирования” Эрика Фримена и Элизабет Робсон).
Данное определение выглядит немного запутанным, поэтому нужно нормальным языком раскрыть это определение более подробно. Начнем с религии :)
В мире имеется много религий (конфессий, течений, толков и тд). Как мы могли бы это описать в коде. Ну например, могли бы взять класс Religion и унаследовать от него остальные религии.
Абстрагируемся от лишнего и используем в классе Religion один метод believe().
public abstract class Religion {
public void believe() {
System.out.println("Я верю в Бога-творца этого мира");
}
}
Все остальные религии можем унаследовать от этого класса. Если бы ограничились христианством и даже исламом и иудаизмом, то проблем бы не возникло.
public class Judaism extends Religion {
}
public class Islam extends Religion {
}
public class Christianity extends Religion {
}
Но если подумать про остальные религии, которые есть сейчас (ну или были ранее, но исчезли), то возникнут проблемы как минимум с буддистами. Придется переопределять у них метод believe.
public class Buddhism extends Religion {
@Override
public void believe() {
System.out.println("Есть Бог или боги или нет, это не важно, главное достичь нирваны :)");
};
}
А так как религий много и верят там даже в макаронного монстра (пастафарианство), то во всех этих религиях придется переопределять метод believe.
Но может сделать иначе, вынести этот метод в интерфейс Faith (Вера) и в нем реализовать метод believe и каждому классу имплементировать данный метод (и соответственно реализовать его в каждом классе как пожелаем)? Но тогда мы получим дублирование кода, ну хотя бы у иудеев и христиан. Мусульмане Бога могут на арабском языке написать.
public interface Faith {
public void believe();
}
public class Christianity implements Faith {
@Override
public void believe() {
System.out.println("Я верю в Бога-творца этого мира");
}
}
public class Judaism implements Faith {
@Override
public void believe() {
System.out.println("Я верю в Бога-творца этого мира");
}
}
Реализацией метода по умолчанию тут сильно не поможешь, религий так много, что даже определив по умолчанию этот метод в интерфейсе Faith, придется во всех не монотеистических религиях реализовывать этот метод по своему, и где-то в том числе повторяться.
Что же предлагает паттерн Стратегия нам в этом случае:
он предлагает к текущему интерфейсу создать готовые классы шаблоны, которые останется подставлять в конструктор конкретной религии. Т.е. определить и создать то самое семейство алгоритмов, о котором говорилось в начале определения.
public interface Faith {
public void believe();
}
public class AbrahamicReligion implements Faith {
@Override
public void believe() {
System.out.println("Я верю в Бога-творца этого мира");
}
}
public class BuddismReligion implements Faith {
@Override
public void believe() {
System.out.println("Есть Бог или боги или нет, это не важно, главное достичь нирваны :)");
}
}
public class JediismReligion implements Faith {
@Override
public void believe() {
System.out.println("Да пребудет с вами Сила!");
}
}
И в каждом классе в конструкторе их подставить.
public abstract class Religion {
Faith faith;
}
public class Judaism extends Religion{
public Judaism() {
this.faith = new AbrahamicReligion();
}
}
public class Christianity extends Religion{
public Christianity() {
this.faith = new AbrahamicReligion();
}
}
public class Buddhism extends Religion {
public Buddhism() {
this.faith = new BuddismReligion();
}
}
Таким образом, при необходимости включить в свой проект еще одну религию, не придется переопределять всем классам метод believe или части классов. А нужно будет только реализовать недостающий класс (если такового нет уже), имплементирующий интерфейс Faith и добавить этот класс в конструкторе новой религии.
public class PastafarianismReligion implements Faith{
@Override
public void believe() {
System.out.println("Кто съел мои макароны???");
}
}
public class Pastafarianism extends Religion {
public Pastafarianism() {
this.faith = new PastafarianismReligion();
}
}
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
нафигможно убрать интерфейс Religion, чтобы не путал :) и обойтись без него....И тогда лучше видно будет что именно в приоритете композиция наследованию. Поправьте меня пожалуйста если не прав :)