JavaRush/Java блог/Java Developer/Шаблон Стратегия (Strategy)

Шаблон Стратегия (Strategy)

Статья из группы Java Developer
участников
Паттерн «Стратегия» определяет семейство алгоритмов, инкапсулирует каждый из них и обеспечивает их взаимозаменяемость. Он позволяет модифицировать алгоритмы независимо от их использования на стороне клиента (это определение взято из книги “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();
    }
}
Комментарии (2)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Anonymous #2691175
Уровень 1
14 июня 2021, 19:49
А почему бы не наследовать вместо Religion конкретную религию, которая, в свою очередь, наследует Religion с интерфейсом Faith?
Жуков Алексей Васильевич Java Developer в КУ Социальная защита
15 июня 2021, 12:48
Если я правильно понял вопрос, то предлагается следующий вариант, верно? т.е. есть религия, которая наследуется от конкретной "общей группы религий", имеющей одинаковую реализацию метода believe:
public class Christianity extends AbrahamicReligion {
}
а уже та наследуется от Religion с интерфейсом Faith
public class AbrahamicReligion extends Religion {
    @Override
    public void believe() {
        System.out.println("Я верю в Бога-творца этого мира");
    }
}
Может я не правильно понял, но в этом случае мы все религии авраамические (иудаизм, христианство и ислам, не говоря уже о сектах и других деноминациях типа свидетелей Иегова) будем наследовать от AbrahamicReligion:
public class Judaism extends AbrahamicReligion {
}
public class Christianity extends AbrahamicReligion {
}
public class Islam extends AbrahamicReligion {
}
и тд.
Возможно и так можно сделать, но как минус, если я правильно понял книгу Эрика Фримена в том, что нужно делать приоритет композиции вместо наследования. И в этом примере мы именно наследование выбираем вместо того, чтобы сделать композицию (расширить класс), добавив в класс Faith faith; По идее в том примере, что в статье используется вообще нафиг можно убрать интерфейс Religion, чтобы не путал :) и обойтись без него....И тогда лучше видно будет что именно в приоритете композиция наследованию. Поправьте меня пожалуйста если не прав :)