JavaRush /Java блог /Random UA /Шаблон Стратегія (Strategy)

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

Стаття з групи Random UA
Паттерн «Стратегія» визначає сімейство алгоритмів, інкапсулює кожен із них та забезпечує їх взаємозамінність. Він дозволяє модифікувати алгоритми незалежно від їх використання на стороні клієнта (це визначення взято з книги "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();
    }
}
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ