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
А почему бы не наследовать вместо Religion конкретную религию, которая, в свою очередь, наследует Religion с интерфейсом Faith?