Шаблон проектирования (шаблон проектирования) — это набор классифицированных и каталогизированных сводок опыта проектирования кода, который используется неоднократно и известен большинству людей.
Шаблоны проектирования делятся на три типа, всего 23 типа:
Написано со ссылкой на статьи в Hongyang, Rookie Tutorials и т. д. Пожалуйста, исправьте меня, если есть какие-либо ошибки. Если есть какие-либо нарушения, пожалуйста, свяжитесь со мной, чтобы удалить их.
1. Шаблон проектирования Observer Pattern (Шаблон наблюдателя). В качестве примера взят общественный сервис WeChat.
2. Шаблон проектирования Factory Pattern (Фабричный шаблон) начинается с продажи Roujiamo.
3. Шаблон проектирования Singleton Шаблон проектирования (шаблон Singleton) Полный анализ
4. Паттерн проектирования Паттерн Стратегия (Strategy Pattern) берет за основу ролевые игры.
5. Шаблон проектирования. Шаблон адаптера (Шаблон адаптера). В качестве примера возьмем зарядное устройство для мобильного телефона.
6. Шаблон команды Design Pattern для управления умной бытовой техникой
7. Шаблон декоратора шаблона дизайна возвращает вас в легендарный мир.
8. Режим дизайна, режим внешнего вида (узор фасада), режим фильма в один клик.
9. Шаблон проектирования. Шаблон метода. Шаблон показывает день программиста.
10. Паттерн проектирования State Pattern (Шаблон состояний) Возьмем в качестве примера торговый автомат.
11. Шаблон проектирования Builder Pattern (Шаблон Builder) Возьмем в качестве примера постройку автомобиля и его покупку.
12. Шаблон проектирования «Прототип шаблона» (шаблон прототипа). В качестве примера возьмем получение нескольких фигур.
13. Шаблон проектирования Flyweight Pattern (Шаблон Приспособленца) в качестве примера берет случайное получение нескольких фигур.
14. Design Pattern Proxy Pattern (Прокси-шаблон) На примере получения картинок с диска
15. Схема оформления Bridge Pattern (Узор моста) В качестве примера возьмем рисование кругов разного цвета.
16. Шаблон комбинации шаблонов проектирования (Композитный шаблон). В качестве примера рассмотрим создание и печать иерархии сотрудников.
17. Шаблон проектирования «Шаблон итератора» (Шаблон «Итератор»). В качестве примера возьмем использование итератора для печати имен.
18. Шаблон проектирования Mediator Pattern (Шаблон посредника) На примере публичных чатов
19. Шаблон проектирования Memento Pattern (Паттерн Memento) Возьмем в качестве примера использование Memento.
20. Паттерн проектирования Interpreter Pattern (Шаблон интерпретатора) Возьмите объяснение предложения в качестве примера
21. Шаблон проектирования цепочки ответственности (Chain of Responsibility Pattern) В качестве примера возьмем печать журналов в Android Studio.
22. Паттерн проектирования Visitor Pattern (Шаблон Посетителя) В качестве примера возьмем отображение компонентов компьютера.
- наблюдатель
- Фабрика
- Синглтон
- Стратегия
- Адаптер
- Команда
- Декоратор
- Фасад
- Шаблонный метод
- Состояние
- Строитель
- Прототип
- Наилегчайший вес
- Прокси
- Мост
- Композитный
- Итератор
- Посредник
- Сувенир
- Цепочка ответственности
- Посетитель
Определяет зависимости между объектами «один ко многим», поэтому при изменении объекта все его зависимости будут уведомлены и автоматически обновлены.
В JDK или Andorid есть много мест, которые реализуют шаблон наблюдателя, например XXXView.addXXXListener. Конечно, XXXView.setOnXXListener не обязательно является шаблоном наблюдателя, поскольку шаблон наблюдателя представляет собой отношение «один ко многим», а для setXXListener. это одна пара. Отношения между 1 и 1 следует называть обратным вызовом.
Специальный интерфейс: subject.java;
/**
* 注册一个观察者
*/
public void registerObserver ( Observer observer );
/**
* 移除一个观察者
*/
public void removeObserver ( Observer observer );
/**
* 通知所有观察者
*/
public void notifyObservers ();
Класс реализации учетной записи 3D-сервиса: ObjectFor3D.java
@ Override
public void registerObserver ( Observer observer ) {
observers . add ( observer );
}
@ Override
public void removeObserver ( Observer observer ) {
int index = observers . indexOf ( observer );
if ( index >= 0 ) {
observers . remove ( index );
}
}
@ Override
public void notifyObservers () {
for ( Observer observer : observers ) {
observer . update ( msg );
}
}
/**
* 主题更新信息
*/
public void setMsg ( String msg ) {
this . msg = msg ;
notifyObservers ();
}
Всем наблюдателям необходимо реализовать этот интерфейс: Observer.java.
public ObserverUser1 ( Subject subject ) {
subject . registerObserver ( this );
}
@ Override
public void update ( String msg ) {
Log . e ( "-----ObserverUser1 " , "得到 3D 号码:" + msg + ", 我要记下来。" );
}
Финальный тест: ObserverActivity.java
// 创建服务号
objectFor3D = new ObjectFor3D ();
// 创建两个订阅者
observerUser1 = new ObserverUser1 ( objectFor3D );
observerUser2 = new ObserverUser2 ( objectFor3D );
// 两个观察者,发送两条信息
objectFor3D . setMsg ( "201610121 的3D号为:127" );
objectFor3D . setMsg ( "20161022 的3D号为:000" );
Кратко перечислим семейство этого паттерна:
1. Статический заводской режим
2. Простой заводской режим (покупка Руджиамо в магазине)
public RoujiaMo creatRoujiaMo ( String type ) {
RoujiaMo roujiaMo = null ;
switch ( type ) {
case "Suan" :
roujiaMo = new ZSuanRoujiaMo ();
break ;
case "La" :
roujiaMo = new ZLaRoujiaMo ();
break ;
case "Tian" :
roujiaMo = new ZTianRoujiaMo ();
break ;
default : // 默认为酸肉夹馍
roujiaMo = new ZSuanRoujiaMo ();
break ;
}
return roujiaMo ;
}
3. Модель фабричного метода (открытие филиала)
Предоставляет абстрактные методы для создания Roujia MoStore: RoujiaMoStore.java.
public abstract RoujiaMo sellRoujiaMo ( String type );
Конкретная реализация абстрактного метода: XianRoujiaMoStore.java.
В ветке по-прежнему используется простая фабричная модель: XianSimpleRoujiaMoFactory.java.
4. Абстрактная фабричная выкройка (с использованием официального сырья)
/**
* 准备工作
*/
public void prepare ( RoujiaMoYLFactory roujiaMoYLFactory ) {
Meet meet = roujiaMoYLFactory . creatMeet ();
YuanLiao yuanLiao = roujiaMoYLFactory . creatYuanLiao ();
Log . e ( "---RoujiaMo:" , "使用官方的原料 ---" + name + ": 揉面-剁肉-完成准备工作 yuanLiao:" + meet + "yuanLiao:" + yuanLiao );
}
Одноэлементный режим предназначен главным образом для того, чтобы избежать пустой траты ресурсов, вызванной созданием нескольких экземпляров, а несколько экземпляров могут легко привести к неверным результатам из-за множественных вызовов. Использование одноэлементного режима может гарантировать, что во всем приложении будет только один экземпляр .
Определение: Для обеспечения уникальности объекта необходимы всего три шага.
Сравните определение:
Стиль «Голодный Хан» [доступен]: SingletonEHan.java
Содержит стиль ленивого Хана [рекомендуется двойная блокировка проверки]: SingletonLanHan.java
private SingletonLanHan () {}
private static SingletonLanHan singletonLanHanFour ;
public static SingletonLanHan getSingletonLanHanFour () {
if ( singletonLanHanFour == null ) {
synchronized ( SingletonLanHan . class ) {
if ( singletonLanHanFour == null ) {
singletonLanHanFour = new SingletonLanHan ();
}
}
}
return singletonLanHanFour ;
}
Шаблон стратегии: определяет семейство алгоритмов и инкапсулирует их отдельно, чтобы сделать их взаимозаменяемыми. Этот шаблон позволяет вносить изменения в алгоритмы независимо от клиентов, которые их используют.
RoleA roleA = new RoleA ( "---A" );
roleA . setiDisplayBehavior ( new DisplayYZ ())
. setiAttackBehavior ( new AttackXL ())
. setiDefendBehavior ( new DefendTMS ())
. setiRunBehavior ( new RunJCTQ ());
roleA . display (); // 样子
roleA . attack (); // 攻击
roleA . run (); // 逃跑
roleA . defend (); // 防御
Определение: Преобразование интерфейса класса в другой интерфейс, ожидаемый клиентом. Адаптер позволяет классам с несовместимыми интерфейсами взаимодействовать друг с другом. Это определение приемлемо, поскольку оно гласит, что функция адаптера заключается в преобразовании одного интерфейса в другой интерфейс.
Возьмем, к примеру, зарядное устройство: зарядные устройства для мобильных телефонов обычно имеют напряжение около 5 В. Напряжение переменного тока в нашем доме в Китае составляет 220 В, поэтому для зарядки мобильного телефона требуется адаптер (редуктор напряжения).
Мобильный телефон: Mobile.java.
В телефоне используется интерфейс, обеспечивающий напряжение 5 В: V5Power.java.
У нас есть бытовой переменный ток 220 В: V220Power.java.
Адаптер для выполнения функции преобразования 220 В в 5 В : V5PowerAdapter.java
Финальный тест: Зарядка телефона:
Mobile mobile = new Mobile ();
V5Power v5Power = new V5PowerAdapter ( new V200Power ());
mobile . inputPower ( v5Power );
Определение: инкапсулируйте «запросы» в объекты, чтобы другие объекты можно было параметризовать с использованием различных запросов, очередей или журналов. Командный режим также поддерживает отменяемые операции. (Упрощение: инкапсулируйте запрос в объект и отделите инициатора действия и исполнителя действия.)
QuickCommand quickCloseCommand = new QuickCommand ( new Command []{ new LightOffCommand ( light ), new ComputerOffCommand ( computer ), new DoorCloseCommand ( door )});
controlPanel . setCommands ( 6 , quickOpenCommand );
controlPanel . keyPressed ( 6 );
controlPanel . setCommands ( 0 , new DoorOpenCommand ( door )); // 开门
controlPanel . keyPressed ( 0 );
Шаблон декоратора: для расширения функциональности декораторы предоставляют более гибкую альтернативу интеграции, динамически назначая обязанности объектам.
Давайте кратко опишем, где вступает в действие шаблон декоратора. Когда мы проектируем класс, нам нужно добавить в этот класс некоторые вспомогательные функции, и мы не хотим менять код этого класса. Именно здесь вступает в силу шаблон декоратора. играть. Пришло время. Это также отражает принцип: классы должны быть открыты для расширений и закрыты для модификаций.
Требования: Разработать систему экипировки игры. Основное требование — уметь рассчитывать силу атаки и описание каждого снаряжения после внедрения различных драгоценных камней:
1. Суперкласс оборудования: IEquip.java.
2. Классы реализации каждого оборудования:
3. Суперкласс декораций (декорации тоже относятся к экипировке): IEquipDecorator.java
4. Класс реализации украшений:
5. Финальный тест: подсчитайте силу атаки и просмотрите описание:
Log . e ( "---" , "一个镶嵌2颗红宝石,1颗蓝宝石的靴子: " );
IEquip iEquip = new RedGemDecorator ( new RedGemDecorator ( new BlueGemDecorator ( new ShoeEquip ())));
Log . e ( "---" , "攻击力:" + iEquip . caculateAttack ());
Log . e ( "---" , "描述语:" + iEquip . description ());
Определение: Обеспечьте унифицированный интерфейс для доступа к группе интерфейсов в подсистеме. Внешний вид определяет интерфейс высокого уровня, упрощающий использование подсистемы. Фактически для удобства клиентов группа операций инкапсулирована в метод.
Спрос: Я предпочитаю смотреть фильмы, поэтому купил проектор, компьютер, стереосистему, спроектировал освещение в комнате и купил аппарат для попкорна. Потом, когда я хочу посмотреть фильм, мне нужен просмотр в один клик и выключение в один клик. .
Переключение и другие операции для каждого класса устройств:
например: Машина для попкорна: PopcornPopper.java
Класс кино: HomeTheaterFacade.java
/**
* 一键观影
*/
public void watchMovie () {
computer . on ();
light . down ();
popcornPopper . on ();
popcornPopper . makePopcorn ();
projector . on ();
projector . open ();
player . on ();
player . make3DListener ();
}
Финальный тест: просмотр фильма в один клик:
new HomeTheaterFacade ( computer , light , player , popcornPopper , projector ). watchMovie ();
Определение: определяет скелет алгоритма и передает некоторые шаги подклассам. Метод шаблона позволяет подклассам переопределять шаги алгоритма без изменения структуры алгоритма.
Требования: Кратко опишите: В нашей компании есть программисты, тестировщики, HR, менеджеры проектов и т.д. Режим шаблонного метода используется ниже для записи статуса работы всего персонала.
Три типа ролей в шаблоне метода шаблона
1. Конкретный метод
2. Абстрактный метод
3. Метод крючка
Суперкласс рабочего: Worker.java
// 具体方法
public final void workOneDay () {
Log . e ( "workOneDay" , "-----------------work start----------------" );
enterCompany ();
work ();
exitCompany ();
Log . e ( "workOneDay" , "-----------------work end----------------" );
}
// 工作 抽象方法
public abstract void work ();
// 钩子方法
public boolean isNeedPrintDate () {
return false ;
}
private void exitCompany () {
if ( isNeedPrintDate ()) {
Log . e ( "exitCompany" , "---" + new Date (). toLocaleString () + "--->" );
}
Log . e ( "exitCompany" , name + "---离开公司" );
}
Класс реализации программиста (время может быть известно): ITWorker.java
/**
* 重写父类的此方法,使可以查看离开公司时间
*/
@ Override
public boolean isNeedPrintDate () {
return true ;
}
Финальный тест:
Посмотрите, как все работают:
QAWorker qaWorker = new QAWorker ( "测试人员" );
qaWorker ();
HRWorker hrWorker = new HRWorker ( "莉莉姐" );
hrWorker . workOneDay ();
...
Проверьте, когда программист ушел из компании:
ITWorker itWorker = new ITWorker ( "jingbin" );
itWorker . workOneDay ();
Определение: позволяет объекту изменять свое поведение при изменении его внутреннего состояния, благодаря чему объект выглядит так, как будто он изменил свой класс.
Определение снова начинает размываться. Подумайте об этом. Когда меняется внутреннее состояние объекта, его поведение меняется вместе с изменением состояния. Похоже, что аналогичный объект повторно инициализируется.
Требования: В качестве примера возьмем торговый автомат (есть такие статусы, как вставленная и невставленная монета, а также способы вставки и возврата монет)
Первоначальная реализация торгового автомата, которую необходимо улучшить: VendingMachine.java.
Улучшенный торговый автомат (более гибкий): VendingMachineBetter.java.
// 放钱
public void insertMoney () {
currentState . insertMoney ();
}
// 退钱
public void backMoney () {
currentState . backMoney ();
}
// 转动曲柄
public void turnCrank () {
currentState . turnCrank ();
if ( currentState == soldState || currentState == winnerState ) {
currentState . dispense (); //两种情况会出货
}
}
// 出商品
public void dispense () {
Log . e ( "VendingMachineBetter" , "---发出一件商品" );
if ( count > 0 ) {
count --;
}
}
// 设置对应状态
public void setState ( State state ) {
this . currentState = state ;
}
Интерфейс состояния: State.java
Класс реализации интерфейса, соответствующий состоянию:
Улучшен тест торгового автомата:
// 初始化售货机,且里面有3个商品
VendingMachineBetter machineBetter = new VendingMachineBetter ( 3 );
machineBetter . insertMoney ();
machineBetter . turnCrank ();
Режим строительства — это режим создания объекта. Режим конструирования может отделить внутреннее представление продукта от процесса производства продукта, так что процесс конструирования может генерировать объекты продукта с различными внутренними представлениями.
Требования: Пользователи идут в автомагазин, чтобы купить машину.
Анализ: Автомагазин подбирает соответствующие автомобили в соответствии с потребностями каждого пользователя.
Суперкласс Строителя: Строитель
public abstract class Builder {
public abstract void setPart ( String name , String type );
public abstract Product getProduct ();
}
Соответствующий класс реализации Builder: ConcreteBuilder
public class ConcreteBuilder extends Builder {
private Product product = new Product ();
@ Override
public void setPart ( String name , String type ) {
product . setName ( name );
product . setType ( type );
}
@ Override
public Product getProduct () {
return product ;
}
}
Менеджер магазина забирает машину:
// 店长
Director director = new Director ();
// 得到宝马汽车,内部实现提取宝马汽车的详情操作
Product product = director . getBProduct ();
// 展示汽车信息
product . showProduct ();
Шаблон прототипа используется для создания повторяющихся объектов, обеспечивая при этом производительность. Этот тип шаблона проектирования представляет собой шаблон создания, который обеспечивает оптимальный способ создания объектов.
Этот шаблон реализует интерфейс прототипа, который используется для создания клона текущего объекта. Этот режим используется, когда стоимость непосредственного создания объектов относительно высока. Например, объект необходимо создать после дорогостоящей операции с базой данных. Мы можем кэшировать объект, возвращать его клон при следующем запросе и обновлять базу данных, когда это необходимо, чтобы сократить количество обращений к базе данных.
Если взять в качестве примера получение нескольких фигур, можно выполнить четыре шага:
1. Создайте абстрактный класс, реализующий интерфейс Cloneable. Форма (реализует возможность клонирования)
public abstract class Shape implements Cloneable {
private String id ;
protected String type ;
public abstract void draw ();
public String getId () {
return id ;
}
public void setId ( String id ) {
this . id = id ;
}
@ Override
public Object clone () {
Object object = null ;
try {
object = super . clone ();
} catch ( CloneNotSupportedException e ) {
Log . e ( "--" , e . getMessage ());
}
return object ;
}
}
2. Создайте класс сущности, расширяющий указанный выше абстрактный класс. Круг, Прямоугольник, Квадрат
public class Circle extends Shape {
public Circle () {
type = "Circle" ;
}
@ Override
public void draw () {
Log . e ( "---" , "Inside Circle::draw() method." );
}
}
3. Создайте класс, чтобы получать классы сущностей из базы данных и сохранять их в хэш-таблице. ШейпКэш
public class ShapeCache {
private static Hashtable < String , Shape > shapeMap = new Hashtable < String , Shape >();
public static Shape getShape ( String shapeId ) {
Shape shapeCache = shapeMap . get ( shapeId );
return ( Shape ) shapeCache . clone ();
}
// 对每种形状都运行数据库查询,并创建该形状
// shapeMap.put(shapeKey, shape);
// 例如,我们要添加三种形状
public static void loadCache () {
Circle circle = new Circle ();
circle . setId ( "1" );
shapeMap . put ( circle . getId (), circle );
Rectangle rectangle = new Rectangle ();
rectangle . setId ( "2" );
shapeMap . put ( rectangle . getId (), rectangle );
Square square = new Square ();
square . setId ( "3" );
shapeMap . put ( square . getId (), square );
}
}
4. Используйте класс ShapeCache, чтобы получить клон фигуры, хранящейся в хеш-таблице.
// 使用 ShapeCache 类来获取存储在 Hashtable 中的形状的克隆。
ShapeCache . loadCache ();
Shape shapeCache1 = ShapeCache . getShape ( "1" );
Shape shapeCache2 = ShapeCache . getShape ( "2" );
Shape shapeCache3 = ShapeCache . getShape ( "3" );
В основном используется для уменьшения количества создаваемых объектов, чтобы уменьшить использование памяти и повысить производительность. Этот тип шаблона проектирования представляет собой структурный шаблон, который позволяет уменьшить количество объектов и тем самым улучшить структуру объектов, необходимую приложению.
Шаблон «Приспособленный вес» пытается повторно использовать существующие объекты того же типа или создавать новые объекты, если соответствующий объект не найден. Мы продемонстрируем этот шаблон, создав 5 объектов, которые рисуют 20 кругов в разных местах. Поскольку доступно только 5 цветов, свойство цвета используется для проверки существующего объекта Circle.
Если взять в качестве примера случайное получение нескольких фигур, можно выполнить четыре шага:
1. Создайте интерфейс.
public interface Shape {
void draw ();
}
2. Создайте класс сущности, реализующий интерфейс.
public class Circle implements Shape {
private String color ;
private int x ;
private int y ;
private int radius ;
public Circle ( String color ) {
this . color = color ;
}
public void setX ( int x ) {
this . x = x ;
}
public void setY ( int y ) {
this . y = y ;
}
public void setRadius ( int radius ) {
this . radius = radius ;
}
@ Override
public void draw () {
Log . e ( "---" , "Circle: Draw() [Color : " + color
+ ", x : " + x + ", y :" + y + ", radius :" + radius );
}
}
3. Создать фабрику для генерации объектов классов сущностей на основе заданной информации.
public class ShapeFactory {
private static final HashMap < String , Shape > circleMap = new HashMap < String , Shape >();
public static Shape getShape ( String color ) {
Shape shape = circleMap . get ( color );
if ( shape == null ) {
shape = new Circle ( color );
circleMap . put ( color , shape );
Log . e ( "getShape" , "Creating circle of color : " + color );
}
return shape ;
}
}
4. Используйте эту фабрику для получения объекта класса сущности путем передачи информации о цвете.
for ( int i = 0 ; i < 20 ; i ++) {
Circle circle = ( Circle ) ShapeFactory . getShape ( getRandomColor ());
circle . setX ( getRandomX ());
circle . setY ( getRandomY ());
circle . setRadius ( 100 );
circle . draw ();
}
Один класс представляет функциональность другого класса. В шаблоне прокси мы создаем объекты на основе существующих объектов, чтобы обеспечить функциональный интерфейс с внешним миром. Можно понять, что если такого объекта в памяти нет, то он будет создан, а если есть, то объект будет возвращен напрямую.
На примере получения изображений с диска всего три шага:
1. Создайте интерфейс.
public interface Image {
void display ();
}
2. Создайте класс сущности RealImage, реализующий интерфейс. Соответствующий класс прокси: ProxyImage.
public class RealImage implements Image {
private String fileName ;
public RealImage ( String fileName ) {
this . fileName = fileName ;
loadFromDisk ( fileName );
}
private void loadFromDisk ( String fileName ) {
Log . e ( "RealImage" , "loading " + fileName );
}
@ Override
public void display () {
Log . e ( "RealImage" , "Displaying " + fileName );
}
}
public class ProxyImage implements Image {
private String fileName ;
private RealImage realImage ;
public ProxyImage ( String fileName ) {
this . fileName = fileName ;
}
@ Override
public void display () {
if ( realImage == null ) {
realImage = new RealImage ( fileName );
}
realImage . display ();
}
}
3. По запросу используйте ProxyImage для получения объекта класса RealImage.
Image image = new ProxyImage ( "test_10mb.png" );
// 第一次是new的,图像从磁盘加载
image . display ();
// 第二次取缓存,图像不需要从磁盘加载
image . display ();
Bridge используется для разделения абстракции и реализации, чтобы они могли изменяться независимо. Этот тип шаблона проектирования представляет собой структурный шаблон, который отделяет абстракцию и реализацию, обеспечивая связующую структуру между ними.
На примере рисования кругов разного цвета реализация разделена на пять этапов:
1. Создайте интерфейс реализации моста.
public interface DrawAPI {
void drawCircle ( int radius , int x , int y );
}
2. Создайте класс реализации моста сущности, реализующий интерфейс DrawAPI. КрасныйКруг, ЗеленыйКруг
public class RedCircle implements DrawAPI {
@ Override
public void drawCircle ( int radius , int x , int y ) {
Log . e ( "---" , "Drawing Circle[ color: red, radius: "
+ radius + ", x: " + x + ", " + y + "]" );
}
}
3. Используйте интерфейс DrawAPI для создания абстрактного класса Shape.
public abstract class Shape {
protected DrawAPI drawAPI ;
protected Shape ( DrawAPI drawAPI ) {
this . drawAPI = drawAPI ;
}
public abstract void draw ();
}
4. Создайте класс сущности, реализующий интерфейс Shape.
public class Circle extends Shape {
private int x , y , radius ;
protected Circle ( int x , int y , int radius , DrawAPI drawAPI ) {
super ( drawAPI );
this . x = x ;
this . y = y ;
this . radius = radius ;
}
@ Override
public void draw () {
drawAPI . drawCircle ( radius , x , y );
}
}
5. Используйте классы Shape и DrawAPI, чтобы рисовать круги разных цветов.
// 画红圆
Circle circle = new Circle ( 10 , 10 , 100 , new RedCircle ()); s
circle . draw ();
// 画绿圆
Circle circle2 = new Circle ( 20 , 20 , 100 , new GreenCircle ());
circle2 . draw ();
Также называемый шаблоном «часть-целое», он используется для обработки группы похожих объектов как одного объекта. Режим композиции объединяет объекты в соответствии с древовидной структурой, которая используется для представления части и целых иерархий. Этот тип шаблона проектирования представляет собой структурный шаблон, который создает древовидную структуру групп объектов.
В качестве примера возьмем создание и печать иерархии сотрудников, пример наименьшей единицы:
1. Создайте класс «Сотрудник» со списком объектов «Сотрудник».
public class Employee {
private String name ;
// 部门
private String dept ;
// 工资
private int salary ;
// 员工 list
private List < Employee > subordinates ;
public Employee ( String name , String dept , int salary ) {
this . name = name ;
this . dept = dept ;
this . salary = salary ;
this . subordinates = new ArrayList < Employee >();
}
public void add ( Employee e ) {
subordinates . add ( e );
}
public void remove ( Employee e ) {
subordinates . remove ( e );
}
public List < Employee > getSubordinates () {
return subordinates ;
}
@ Override
public String toString () {
return "Employee{" +
"name='" + name + ''' +
", dept='" + dept + ''' +
", salary=" + salary +
", subordinates=" + subordinates +
'}' ;
}
}
2. Используйте класс «Сотрудник», чтобы создать и распечатать иерархию сотрудников.
final Employee ceo = new Employee ( "John" , "CEO" , 30000 );
Employee headSales = new Employee ( "Robert" , "Head sales" , 20000 );
Employee headMarketing = new Employee ( "Michel" , "Head Marketing" , 20000 );
Employee clerk1 = new Employee ( "Laura" , "Marketing" , 10000 );
Employee clerk2 = new Employee ( "Bob" , "Marketing" , 10000 );
Employee salesExecutive1 = new Employee ( "Richard" , "Sales" , 10000 );
Employee salesExecutive2 = new Employee ( "Rob" , "Sales" , 10000 );
ceo . add ( headSales );
ceo . add ( headMarketing );
headSales . add ( salesExecutive1 );
headSales . add ( salesExecutive2 );
headMarketing . add ( clerk1 );
headMarketing . add ( clerk2 );
Log . e ( "---" , ceo . toString ());
// 打印
/*
* Employee{name='John', dept='CEO', salary=30000,
* subordinates=[Employee{name='Robert', dept='Head sales', salary=20000,
* subordinates=[
* Employee{name='Richard', dept='Sales', salary=10000, subordinates=[]},
* Employee{name='Rob', dept='Sales', salary=10000, subordinates=[]}]},
* Employee{name='Michel', dept='Head Marketing', salary=20000,
* subordinates=[Employee{name='Laura', dept='Marketing', salary=10000, subordinates=[]},
* Employee{name='Bob', dept='Marketing', salary=10000, subordinates=[]}]}]}
*/
Очень распространенный шаблон проектирования в средах программирования Java и .Net. Этот шаблон используется для последовательного доступа к элементам объекта коллекции без знания базового представления объекта коллекции. Шаблон итератора — это поведенческий шаблон.
Если взять в качестве примера использование итератора для печати имен, то всего необходимо выполнить три шага:
1. Создайте интерфейс:
public interface Iterator {
public boolean hasNext ();
public Object next ();
}
public interface Container {
public Iterator getIterator ();
}
2. Создайте класс сущности, реализующий интерфейс контейнера. Этот класс имеет внутренний класс NameIterator, реализующий интерфейс Iterator.
public class NameRepository implements Container {
private String names [] = { "John" , "jingbin" , "youlookwhat" , "lookthis" };
@ Override
public Iterator getIterator () {
return new NameIterator ();
}
private class NameIterator implements Iterator {
int index ;
@ Override
public boolean hasNext () {
if ( index < names . length ) {
return true ;
}
return false ;
}
@ Override
public Object next () {
if ( hasNext ()) {
return names [ index ++];
}
return null ;
}
}
}
3. Используйте NameRepository, чтобы получить итератор и напечатать имя.
NameRepository nameRepository = new NameRepository ();
for ( Iterator iterator = nameRepository . getIterator (); iterator . hasNext (); ) {
String name = ( String ) iterator . next ();
Log . e ( "---" , name );
/*
* /---: John
* /---: jingbin
* /---: youlookwhat
* /---: lookthis
*/
}
Используется для уменьшения сложности связи между несколькими объектами и классами. Этот шаблон предоставляет промежуточный класс, который обычно управляет связью между различными классами и поддерживает слабую связь, что упрощает поддержку кода. Модель посредника – это поведенческая модель.
Если взять в качестве примера общедоступный чат, то минимальные шаги для примера:
1. Создайте промежуточный класс.
public class CharRoom {
public static void showMessage ( User user , String message ) {
Log . e ( "---" , new Date (). toString ()
+ " [" + user . getName () + "] : " + message );
}
}
2. Создайте класс пользователя.
public class User {
private String name ;
public User ( String name ) {
this . name = name ;
}
public String getName () {
return name ;
}
public void setName ( String name ) {
this . name = name ;
}
public void sendMessage ( String message ) {
// 使用中介类
CharRoom . showMessage ( this , message );
}
}
3. Используйте объект «Пользователь», чтобы отобразить связь между ними.
User jingbin = new User ( "jingbin" );
jingbin . sendMessage ( "Hi~ youlookwhat!" );
//---: Sun Feb 02 08:11:47 GMT+00:00 2020 [jingbin] : Hi~ youlookwhat!
User jingbin = new User ( "youlookwhat" );
jingbin . sendMessage ( "Hi~ jingbin!" );
//---: Sun Feb 02 08:11:49 GMT+00:00 2020 [youlookwhat] : Hi~ jingbin!
Сохраните определенное состояние объекта, чтобы его можно было восстановить в подходящий момент. Режим заметок — это поведенческий режим.
Если взять в качестве примера памятку, то минимальные шаги модуля составляют:
1. Создайте класс Memento.
public class Memento {
private String state ;
public Memento ( String state ) {
this . state = state ;
}
public String getState () {
return state ;
}
public void setState ( String state ) {
this . state = state ;
}
}
2. Создайте класс Originator.
public class Originator {
private String state ;
public String getState () {
return state ;
}
public void setState ( String state ) {
this . state = state ;
}
public Memento setSateToMemento () {
return new Memento ( state );
}
public String getStateFromMemento ( Memento memento ) {
return memento . getState ();
}
}
3. Создайте класс CareTaker.
public class CareTaker {
private List < Memento > mementoList = new ArrayList < Memento >();
public void add ( Memento memento ) {
mementoList . add ( memento );
}
public Memento get ( int index ) {
return mementoList . get ( index );
}
}
4. Используйте объекты CareTaker и Originator.
// 管理者
CareTaker careTaker = new CareTaker ();
Originator originator = new Originator ();
originator . setState ( "State #1" );
originator . setState ( "State #2" );
// 保存状态
careTaker . add ( originator . setSateToMemento ());
originator . setState ( "State #3" );
// 保存状态
careTaker . add ( originator . setSateToMemento ());
originator . setState ( "State #4" );
Log . e ( "---" , "Current State: " + originator . getState ());
// 得到保存的状态
String fromMemento1 = originator . getStateFromMemento ( careTaker . get ( 0 ));
Log . e ( "---" , "First Saved State: " + fromMemento1 );
String fromMemento2 = originator . getStateFromMemento ( careTaker . get ( 1 ));
Log . e ( "---" , "Second Saved State: " + fromMemento2 );
/*
* /---: Current State: State #4
* /---: First Saved State: State #2
* /---: Second Saved State: State #3
*/
Предоставляет способ оценить грамматику или выражения языка и представляет собой поведенческий шаблон. Этот шаблон реализует интерфейс выражения, который интерпретирует определенный контекст. Этот режим используется в синтаксическом анализе SQL, механизмах обработки символов и т. д.
В качестве примера возьмем объяснение предложения, минимальные шаги единицы:
1. Создайте интерфейс выражения Expression.
public interface Expression {
public boolean interpreter ( String content );
}
2. Создайте класс сущности, реализующий указанный выше интерфейс. ТерминальноеВыражение, ИлиВыражение, ИВыражение.
public class TerminalExpression implements Expression {
private String data ;
public TerminalExpression ( String data ) {
this . data = data ;
}
@ Override
public boolean interpreter ( String content ) {
// 是包含判断
return content . contains ( data );
}
}
public class OrExpression implements Expression {
private Expression expression1 ;
private Expression expression2 ;
public OrExpression ( Expression expression1 , Expression expression2 ) {
this . expression1 = expression1 ;
this . expression2 = expression2 ;
}
@ Override
public boolean interpreter ( String content ) {
return expression1 . interpreter ( content ) || expression2 . interpreter ( content );
}
}
public class AndExpression implements Expression {
private Expression expression1 ;
private Expression expression2 ;
public AndExpression ( Expression expression1 , Expression expression2 ) {
this . expression1 = expression1 ;
this . expression2 = expression2 ;
}
@ Override
public boolean interpreter ( String content ) {
return expression1 . interpreter ( content ) && expression2 . interpreter ( content );
}
}
3. Используйте класс Expression для создания правил и их анализа.
/**
* 规则:jingbin 和 youlookwhat 是男性
*/
public static Expression getMaleExpression () {
TerminalExpression jingbin = new TerminalExpression ( "jingbin" );
TerminalExpression youlookwhat = new TerminalExpression ( "youlookwhat" );
return new OrExpression ( jingbin , youlookwhat );
}
/**
* 规则:Julie 是一个已婚的女性
*/
public static Expression getMarriedWomanExpression () {
TerminalExpression julie = new TerminalExpression ( "Julie" );
TerminalExpression married = new TerminalExpression ( "Married" );
return new AndExpression ( julie , married );
}
Expression maleExpression = getMaleExpression ();
// jingbin is male: true
Log . e ( "---" , "jingbin is male: " + maleExpression . interpreter ( "jingbin" ));
Expression womanExpression = getMarriedWomanExpression ();
// Julie is married woman: true
Log . e ( "---" , "Julie is married woman: " + womanExpression . interpreter ( "Married Julie" ));
Шаблон «Цепочка ответственности» создает цепочку объектов-получателей для запроса. Этот шаблон разделяет отправителя и получателя запроса в зависимости от типа запроса. Этот тип шаблона проектирования представляет собой поведенческий шаблон. В этом шаблоне обычно каждый получатель содержит ссылку на другого получателя. Если объект не может обработать запрос, он передает тот же запрос следующему получателю и так далее.
Если взять в качестве примера печать журналов в Android Studio, то минимальные шаги:
1. Создайте абстрактный класс журнала AbstractLogger.
public abstract class AbstractLogger {
public static int INFO = 1 ;
public static int DEBUG = 2 ;
public static int ERROR = 3 ;
protected int level ;
// 责任链中的下一个元素
protected AbstractLogger nextLogger ;
public void setNextLogger ( AbstractLogger nextLogger ) {
this . nextLogger = nextLogger ;
}
public void logMessage ( int level , String message ) {
if ( this . level <= level ) {
write ( message );
}
// 递归效果,不断调用下一级 logMessage
if ( nextLogger != null ) {
nextLogger . logMessage ( level , message );
}
}
protected abstract void write ( String message );
}
2. Создайте класс сущности, расширяющий класс регистратора.
public class ConsoleLogger extends AbstractLogger {
public ConsoleLogger ( int level ) {
this . level = level ;
}
@ Override
protected void write ( String message ) {
Log . e ( "---" , "Standard Console::Logger " + message );
}
}
public class FileLogger extends AbstractLogger {
public FileLogger ( int level ) {
this . level = level ;
}
@ Override
protected void write ( String message ) {
Log . e ( "---" , "File::Logger " + message );
}
}
public class ErrorLogger extends AbstractLogger {
public ErrorLogger ( int level ) {
this . level = level ;
}
@ Override
protected void write ( String message ) {
Log . e ( "---" , "Error Console::Logger " + message );
}
}
3. Создавайте разные типы логгеров. Дайте им разные уровни ошибок и внутри каждого регистратора установите следующий регистратор. Следующий регистратор в каждом регистраторе представляет собой часть цепочки.
public static AbstractLogger getChainOfLoggers () {
ErrorLogger errorLogger = new ErrorLogger ( AbstractLogger . ERROR );
FileLogger fileLogger = new FileLogger ( AbstractLogger . DEBUG );
ConsoleLogger consoleLogger = new ConsoleLogger ( AbstractLogger . INFO );
errorLogger . setNextLogger ( fileLogger );
fileLogger . setNextLogger ( consoleLogger );
return errorLogger ;
}
AbstractLogger logger = getChainOfLoggers ();
// ---: Standard Console::Logger this is an information.
logger . logMessage ( AbstractLogger . INFO , "this is an information." );
// ---: File::Logger this is a debug level information.
// ---: Standard Console::Logger this is a debug level information.
logger . logMessage ( AbstractLogger . DEBUG , "this is a debug level information." );
// ---: Error Console::Logger this is a error level information.
// ---: File::Logger this is a error level information.
// ---: Standard Console::Logger this is a error level information.
logger . logMessage ( AbstractLogger . ERROR , "this is a error level information." );
В шаблоне посетителя мы используем класс посетителя, который меняет алгоритм выполнения класса элемента. Таким образом, алгоритм выполнения элемента может меняться по мере изменения посетителя. Этот тип шаблона проектирования представляет собой поведенческий шаблон. Согласно схеме, объект-элемент принял объект-посетитель, чтобы объект-посетитель мог обрабатывать операции с объектом-элементом.
Если взять в качестве примера компоненты дисплея-компьютера, то он в основном реализуется в пять этапов:
1. Определите интерфейс, представляющий элемент.
public interface ComputerPart {
public void accept ( ComputerPartVisitor computerPartVisitor );
}
2. Создайте класс сущности, расширяющий указанный выше класс. Клавиатура, монитор, мышь, компьютер
public class Computer implements ComputerPart {
private ComputerPart [] parts ;
public Computer () {
this . parts = new ComputerPart []{ new Mouse (), new Keyboard (), new Monitor ()};
}
@ Override
public void accept ( ComputerPartVisitor computerPartVisitor ) {
for ( ComputerPart part : parts ) {
part . accept ( computerPartVisitor );
}
computerPartVisitor . visit ( this );
}
}
public class Mouse implements ComputerPart {
@ Override
public void accept ( ComputerPartVisitor computerPartVisitor ) {
computerPartVisitor . visit ( this );
}
}
3. Определите интерфейс, представляющий посетителей.
public interface ComputerPartVisitor {
public void visit ( Computer computer );
public void visit ( Mouse mouse );
public void visit ( Keyboard keyboard );
public void visit ( Monitor monitor );
}
4. Создайте посетителей сущностей, реализующих указанные выше классы.
public class ComputerPartDisplayVisitor implements ComputerPartVisitor {
@ Override
public void visit ( Computer computer ) {
Log . e ( "---" , "Displaying Computer." );
}
@ Override
public void visit ( Mouse mouse ) {
Log . e ( "---" , "Displaying Mouse." );
}
@ Override
public void visit ( Keyboard keyboard ) {
Log . e ( "---" , "Displaying Keyboard." );
}
@ Override
public void visit ( Monitor monitor ) {
Log . e ( "---" , "Displaying Monitor." );
}
}
5. Используйте ComputerPartDisplayVisitor для отображения компонентов компьютера.
ComputerPart computer = new Computer ();
computer . accept ( new ComputerPartDisplayVisitor ());
/*
*打印:
*---: Displaying Mouse.
*---: Displaying Keyboard.
*---: Displaying Monitor.
*---: Displaying Computer.
*/