Допустим, мы устроились стажерами-разработчиками программного обеспечения в супермаркете. Наша задача — разработать приложение для рынка, в котором перечислены все автомобили, выставленные на продажу.

Мы начали с основ и позволили всем типам автомобилей наследовать класс Car. Метод listManufacturer перечисляет детали и марку производителя автомобиля, и в нашем случае он варьируется от подкласса к подклассу, поэтому он остается абстрактным, а класс Car является абстрактным классом. Методы run и carryPassengers являются обычными и универсальными методами, как показано ниже.

void run() {
    System.out.println("Run from point A to point B");
}
void carryPassengers() {
    System.out.println("Carry passengers and their luggages");
}

Все выглядит идеально, и «Наследие», кажется, решает все наши проблемы здесь, как идеальная часть головоломки.

Но манекен или игрушечная машинка не имеют функции carryPassengers и не должны наследовать метод carryPassengers. В таких сценариях реализуйте следующий принцип объектно-ориентированного проектирования.

Identify aspects of your applications that vary (carryPassengers) and separate them from what remains same. 

Абстрактные методы listManufacturer и run остаются теми же для подклассов, что касается наследования. Пока carryPassengers метода нет. Давайте инкапсулируем изменения, чтобы они не влияли на остальную часть кода.

Мы написали интерфейс Carriable с абстрактным методом carryPassengers. И только те вагоны (классы) реализуют интерфейс, который реально может перевозить пассажиров. Бинго! Мы успешно отделили переменную часть в реализации. Давайте сосредоточимся на обслуживании кода.

Здесь carryPassengers() отдельно и индивидуально реализуется в каждом классе Carriable. (На данный момент давайте проигнорируем другие методы для простоты.)

class TeslaCar extends Car implements Carriable {
   void carryPassengers() {
       System.out.println("Carry passengers and their luggages");
   }
}
class HondaCar extends Car implements Carriable {
   void carryPassengers() {
       System.out.println("Carry passengers and their luggages");
   }
}

А теперь представьте, нам нужно изменить поведение метода carryPassengers, а carryPassengers состоит из 100 строк кода и 100 классов типа Carriable.

Чтобы изменить поведение метода carryPassengers, нам нужно будет коснуться 100 классов типа Carriable индивидуально и по отдельности. Что ж, это ничего не делает, кроме как вызывает ошибки в коде и затрудняет обслуживание кода. Прикосновение к каждому подклассу типа Carriable делает дизайн кода негибким.

Конкретная реализация carryPassengers из интерфейса Carriable была дана в классах Carry и CarryNot.

class Carry implements Carriable {
    public void carryPassengers() {
        System.out.println("Carry passengers and their luggages");
    }
}
Class CarryNot implements Carriable {
   public void carryPassengers() {
       System.out.println("Cannot carry passengers and their     luggages");
    }
}

С другой стороны, абстрактный класс Car имеет переменную экземпляра, переносимую для просмотра конкретного класса Carry или CarryNot и, следовательно, для просмотра различной реализации метода carryPassengers, инкапсулированного в классах Carry и CarryNot.

Каждый подкласс класса Car будет рассматривать Carry или CarryNot через унаследованную переменную экземпляра carriable типа Carriable.

У абстрактного класса Car есть метод setCarriable(Carriable carriable), чтобы решить, на какой тип Carriable смотреть, смотреть ли на класс Carry или CarryNot.

void setCarriable(Carriable carriable) {
    this.carriable = carriable;
}

Каждый подкласс класса Car также наследует метод carryPassengers() от родительского абстрактного класса Car. Метод carryPassengers ничего не делает, кроме метода calls carryPassengers() уже установленного переносимого.

void carryPassengers() {
    carriable.carryPassengers();
}

Теперь, если нам нужно изменить поведение метода carry, нам нужно коснуться только одного подкласса Carry. Этот подход гораздо более гибкий, чем предыдущий подход.

Шаблон разработки стратегии определяет семейство алгоритмов (в нашем случае меняющийся код carryPassengers), инкапсулирует каждый из них (в нашем случае классы Carry и CarryNot) и делает их взаимозаменяемыми (в нашем случае переносимая переменная Carriable может обращаться к классам Carry и CarryNot). Это позволяет алгоритмам изменяться независимо от клиента, который их использует. (В нашем случае мы могли бы изменить поведение carryPassengers, не затрагивая подклассы класса Car).