Допустим, мы устроились стажерами-разработчиками программного обеспечения в супермаркете. Наша задача — разработать приложение для рынка, в котором перечислены все автомобили, выставленные на продажу.
Мы начали с основ и позволили всем типам автомобилей наследовать класс 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
).