Допустим, мы устроились стажерами-разработчиками программного обеспечения в супермаркете. Наша задача — разработать приложение для рынка, в котором перечислены все автомобили, выставленные на продажу.
Мы начали с основ и позволили всем типам автомобилей наследовать класс 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(); }
interface Carriable { | |
void carryPassengers(); | |
} | |
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"); | |
} | |
} | |
abstract class Car { | |
Carriable carriable; | |
abstract void listManufacturer(); | |
void run() { | |
System.out.println("Run from point A to point B"); | |
} | |
void setCarriable(Carriable carriable) { | |
this.carriable = carriable; | |
} | |
void carryPassengers() { | |
carriable.carryPassengers(); | |
} | |
} | |
class HondaCar extends Car { | |
void listManufacturer() { | |
System.out.println("Manufactured by Honda"); | |
} | |
} | |
class DummyCar extends Car { | |
void listManufacturer() { | |
System.out.println("Manufactured by Hot Wheels"); | |
} | |
} | |
class Demo { | |
public static void main(String[] args) { | |
HondaCar hondaCar = new HondaCar(); | |
hondaCar.setCarriable(new Carry()); | |
hondaCar.listManufacturer(); | |
hondaCar.run(); | |
hondaCar.carryPassengers(); | |
System.out.println(); | |
DummyCar dummyCar = new DummyCar(); | |
dummyCar.setCarriable(new CarryNot()); | |
dummyCar.listManufacturer(); | |
dummyCar.run(); | |
dummyCar.carryPassengers(); | |
} | |
} |
Теперь, если нам нужно изменить поведение метода carry
, нам нужно коснуться только одного подкласса Carry
. Этот подход гораздо более гибкий, чем предыдущий подход.
Шаблон разработки стратегии определяет семейство алгоритмов (в нашем случае меняющийся код carryPassengers
), инкапсулирует каждый из них (в нашем случае классы Carry
и CarryNot
) и делает их взаимозаменяемыми (в нашем случае переносимая переменная Carriable может обращаться к классам Carry
и CarryNot
). Это позволяет алгоритмам изменяться независимо от клиента, который их использует. (В нашем случае мы могли бы изменить поведение carryPassengers
, не затрагивая подклассы класса Car
).