设计模式是软件开发中常见问题的解决方案,它们是经过验证的并且经常被重复使用的设计模板
。设计模式提供了一套通用的解决方案,帮助开发人员构建高质量、可维护和可扩展
的代码。设计模式并不是特定于某种编程语言,而是面向对象编程范式的通用原则
这类设计模式关注如何创建对象
,旨在解决对象创建的灵活性和复杂性
。包括了单例模式、工厂模式、抽象工厂模式
、建造者模式和原型模式
只有一个实例
,并提供全局访问点子类决定要实例化的类
复杂对象的构建与其表示分离
,使同样的构建过程可以创建不同的表示这类设计模式关注如何将类和对象组合成更大的结构,以解决系统中不同类和对象之间的灵活性和复杂性问题
。包括了适配器模式、装饰器模式、代理模式、组合模式、外观模式、桥接模式和享元模式
这类设计模式关注对象之间的通信和职责分配,以解决类和对象之间的高效沟通和合作问题
它包括了观察者模式、策略模式、命令模式、模板方法模式、迭代器模式、责任链模式、状态模式和访问者模式
最常用的七种设计模式如下:
软件设计七大原则,也称为SOLID原则
,是一组面向对象设计的指导原则,旨在帮助开发人员编写可维护、可扩展和易于理解的代码。这些原则和设计模式
密切相关,它们共同为面向对象编程提供了一套综合的指导方针。SOLID 是一组面向对象编程和设计原则的首字母缩略词
,即下列前五种设计原则中的首字母
以下是SOLID原则的简要介绍和与设计模式的关系:
单一职责原则(Single Responsibility Principle):
一个类应该只有一个引起变化的原因。这意味着一个类应该只负责一项职责
。遵循单一职责原则可以提高类的内聚性和可维护性
关系设计模式:单例模式、工厂模式、观察者模式
等,这些设计模式都帮助实现单一职责原则,将不同的功能拆分到不同的类中。
开放封闭原则(Open/Closed Principle):
软件实体(类、模块、函数等)应该对扩展开放,对修改封闭
。这意味着应该通过扩展现有的代码来引入新功能,而不是直接修改现有的代码
关系设计模式:策略模式、装饰器模式、观察者模式
等,这些设计模式支持开放封闭原则,通过定义接口、抽象类和组合来实现可扩展性
里氏替换原则(Liskov Substitution Principle):
子类对象应该能够替换其基类对象,而不影响程序的正确性
。即在不改变程序正确性的前提下,派生类可以扩展基类的功能
关系设计模式:工厂模式、模板方法模式
等,这些设计模式利用了子类替换基类的特性,让代码更加灵活和可扩展
接口隔离原则(Interface Segregation Principle):
客户端不应该强迫依赖于它们不使用的接口。一个类不应该依赖于它不需要的接口
关系设计模式:适配器模式、策略模式
等,这些设计模式帮助实现接口隔离原则,将大接口拆分成多个小接口,让类只依赖于需要的接口
依赖倒置原则(Dependency Inversion Principle):
高层模块不应该依赖于低层模块,而是应该依赖于抽象
。抽象不应该依赖于具体实现细节,而是具体实现应该依赖于抽象
关系设计模式:依赖注入、工厂模式
等,这些设计模式遵循依赖倒置原则,通过依赖抽象而不是具体实现来降低模块之间的耦合
合成复用原则(Composition/Aggregation Reuse Principle):
优先使用对象组合或聚合,而不是继承来达到代码复用的目的
。通过组合关系,可以更灵活地增加、删除或替换组件
关系设计模式:装饰器模式、组合模式
等,这些设计模式强调对象组合或聚合,而不是继承来实现代码复用
迪米特法则(Law of Demeter,最少知识原则):
一个对象应该对其他对象有尽可能少的了解。即一个类应该尽量减少与其他类之间的交互,尽量降低耦合性
关系设计模式:外观模式、中介者模式
等,这些设计模式帮助减少对象之间的直接交互,通过引入中间类来降低类之间的耦合度
软件设计原则的目的:
降低对象之间的耦合性和复杂度,提升对象的内聚性,增加程序的可复用性、可扩展性和可维护性
单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取该实例
。这样可以避免多次实例化相同类的对象,节省内存资源,并确保在整个应用程序中只有一个共享的实例被使用
在单例模式中,有几个重要的要点需要考虑:
私有的构造函数: 为了防止在外部类中直接实例化对象,单例类的构造函数应该设为私有的,这样其他类就无法通过 new
操作符创建该类的对象
静态成员变量: 单例类需要一个静态成员变量来保存唯一实例的引用,确保全局唯一性
静态获取方法: 单例类应该提供一个静态的、公共的方法来获取该类的唯一实例。这个方法通常命名为 getInstance()
延迟初始化: 在首次调用 getInstance()
方法时才创建实例,而不是在类加载时就创建实例,这样可以实现延迟初始化,提高性能
线程安全: 需要考虑多线程环境下的并发访问问题。可以使用同步机制来保证线程安全,但需要注意性能开销
package com.zhouquan.entity;
/**
* @author ZhouQuan
* @description 单例模式
* @date 2023-07-23 09:57
**/
public class Singleton {
/**
* 私有的静态成员变量,用于保存唯一实例的引用
*/
private static Singleton instance;
/**
* 私有的构造函数,防止外部类实例化
*/
private Singleton() {
}
/**
* 静态的获取实例方法
*
* @return
*/
public static synchronized Singleton getInstance() {
// 延迟初始化,在首次调用该方法时才创建实例
if (instance == null) {
instance = new Singleton();
}
return instance;
}
/**
* 其他业务方法
*/
public void doSomething() {
System.out.println("其他业务方法...");
}
public static void main(String[] args) {
// 使用单例模式获取实例
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
// 验证是否是同一个实例
if (singleton1 == singleton2) {
System.out.println("相同的实例对象");
} else {
System.out.println("不同的实例对象");
}
}
}
在上面的示例中,Singleton
类的构造函数是私有的,确保其他类无法直接实例化该类。通过 getInstance()
方法获取唯一实例,使用了简单的同步机制来保证线程安全
。请注意,这种简单的同步机制虽然保证了线程安全,但在多线程环境下可能会有一些性能开销
。在实际开发中,可以考虑使用更高效的实现方式,比如基于静态内部类的单例模式
运行以上代码将看到输出结果表明 singleton1
和 singleton2
是同一个实例,证明单例模式确保了类只有一个唯一的实例
以下为单例模式的使用场景,读取系统配置中的两个设置。在项目启动后通过调用获取实例方法获取系统的配置,使得在项目的任何地方都会读取到相同的配置。
需要知道的是,在使用此设置时通过SystemConfig.getInstance() 获取项目配置信息,而非通过在类中增加@configuration注解获取到配置实例,可以提升项目的启动速度
package com.zhouquan.entity;
import lombok.Data;
/**
* @author ZhouQuan
* @desciption 单例模式的使用场景
* @date 2023/7/23 10:17
*/
@Data
public class SystemConfig {
private static SystemConfig instance = null;
/**
* 是否启动定时
*/
private boolean enableTime;
/**
* 是否重试数据同步
*/
private boolean retrySync;
private SystemConfig() {
}
public static SystemConfig getInstance() {
if (instance == null) {
instance = new SystemConfig();
}
return instance;
}
}
优点:
唯一实例
:单例模式确保一个类只有一个实例存在,这样可以防止多次实例化,避免了资源浪费,同时也确保了对象的唯一性,方便对该实例进行全局访问
全局访问
:由于单例模式的实例在整个应用程序中是全局可访问的,因此可以在不同的模块和组件中共享实例,方便数据共享和通信
延迟实例化
:单例模式可以实现延迟实例化,即只在需要的时候才创建实例。这在某些情况下可以节省系统资源,提高系统的启动效率
避免竞态条件
:在多线程环境下,单例模式可以避免由于多个线程同时创建实例而引发的竞态条件(Race Condition),保证实例的唯一性和正确性
缺点:
难以扩展:由于单例模式限制了类只能有一个实例,因此扩展时可能会遇到一些困难。如果需要扩展单例类,可能需要修改原有的代码
单一职责原则问题:单例模式将实例的创建逻辑和实例的职责混合在一起,可能违反了单一职责原则。这样会使得单例类的代码较为复杂
对象生命周期问题:由于单例模式的实例在整个应用程序的生命周期中都存在,可能会导致对象长时间驻留在内存中,增加了内存使用
可测试性问题:由于单例模式在全局范围内访问实例,可能会导致依赖于单例实例的类难以进行单元测试
总体来说,单例模式在某些情况下非常有用,特别是需要确保全局唯一实例和避免资源浪费的场景。然而,在使用单例模式时需要慎重考虑,确保它不会导致代码的复杂性增加,并且不会影响系统的可扩展性和可测试性
工厂模式(Factory Pattern)是一种创建型设计模式,它提供了一种统一的接口来创建对象,而不需要暴露对象的创建逻辑。通过使用工厂模式,可以将对象的实例化过程封装在工厂类中,客户端只需要与工厂类进行交互,从而降低了代码的耦合性,增强了代码的可维护性和可扩展性
工厂模式有三种常见的变体:简单工厂模式、工厂方法模式和抽象工厂模式
Product 是产品接口,定义了产品对象的通用行为 show()
ConcreteProductA 和 ConcreteProductB 分别是具体产品A和具体产品B,它们实现了 Product 接口,并提供了自己的具体实现
SimpleFactory 是简单工厂类,包含静态方法 createProduct() 来根据不同的参数创建具体的产品实例
Main 是客户端代码,通过调用 SimpleFactory 的静态方法来获取产品实例并使用产品的 show() 方法
+----------------------+
| Product |
+----------------------+
| <<interface>> |
| show() |
+----------------------+
^
|
|
|
+--------------------------+ +----------------------------+
| | |
+-----------------+ +----------------+ +----------------+
|ConcreteProductA | | SimpleFactory | |ConcreteProductB|
+-----------------+ +----------------+ +----------------+
| show() | | createProduct()| | show() |
+----------------+ +----------------+ +----------------+
^
|
|
|
|
+------------------+
| ConcreteProductA |
+------------------+
| show() |
+------------------+
简单工厂模式并不是标准的设计模式,它更像是一种编程习惯。在简单工厂模式中,我们创建一个工厂类,该工厂类根据不同的参数来创建不同的产品类实例
代码示例:
package com.zhouquan.entity.factory;
/**
* 产品接口
*/
interface Product {
/**
* 商品类需要实现的方法
*/
void show();
}
/**
* 具体产品A
*/
class ConcreteProductA implements Product {
@Override
public void show() {
System.out.println("具体产品A");
}
}
/**
* 具体产品B
*/
class ConcreteProductB implements Product {
@Override
public void show() {
System.out.println("具体产品B");
}
}
/**
* 产品枚举类
*/
enum ProductEnums {
/**
* 产品A
*/
CONCRETE_PRODUCT_A,
/**
* 产品B
*/
CONCRETE_PRODUCT_B
}
/**
* 简单工厂类
*/
class SimpleFactory {
public static Product createProduct(ProductEnums productEnums) {
if (productEnums.equals(ProductEnums.CONCRETE_PRODUCT_A)) {
return new ConcreteProductA();
} else if (productEnums.equals(ProductEnums.CONCRETE_PRODUCT_B)) {
return new ConcreteProductB();
} else {
throw new IllegalArgumentException("无效产品类型");
}
}
}
/**
* @author ZhouQuan
* @description 简单工厂模式(Simple Factory Pattern)
* @date 2023-07-23 10:36
**/
public class SimpleFactoryPattern {
public static void main(String[] args) {
Product productA = SimpleFactory.createProduct(ProductEnums.CONCRETE_PRODUCT_A);
Product productB = SimpleFactory.createProduct(ProductEnums.CONCRETE_PRODUCT_B);
productA.show();
productB.show();
}
}
工厂方法模式将每个具体产品的创建逻辑委托给各自的工厂类。每个具体产品对应一个工厂类,通过实现工厂接口来创建具体产品
代码示例:
package com.zhouquan.entity.factory;
/**
* 产品接口
*/
interface Product {
/**
* 商品类需要实现的方法
*/
void show();
}
/**
* 具体产品A
*/
class ConcreteProductA implements Product {
@Override
public void show() {
System.out.println("具体产品A");
}
}
/**
* 具体产品B
*/
class ConcreteProductB implements Product {
@Override
public void show() {
System.out.println("具体产品B");
}
}
/**
* 工厂接口
*/
interface Factory {
Product createProduct();
}
/**
* 具体工厂A
*/
class ConcreteFactoryA implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductA();
}
}
/**
* 具体工厂B
*/
class ConcreteFactoryB implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductB();
}
}
/**
* @author ZhouQuan
* @desciption 工厂方法模式(Factory Method Pattern)
* @date 2023/7/23 11:35
*/
public class FactoryMethodPattern {
public static void main(String[] args) {
Factory factoryA = new ConcreteFactoryA();
Factory factoryB = new ConcreteFactoryB();
Product productA = factoryA.createProduct();
Product productB = factoryB.createProduct();
productA.show();
productB.show();
}
}
抽象工厂模式提供一个接口,用于创建一系列相关或相互依赖的产品对象。每个具体工厂实现该接口,用于创建一组产品对象
代码示例:
package com.zhouquan.entity.factory;
/**
* 产品接口
*/
interface Product {
/**
* 商品类需要实现的方法
*/
void show();
}
/**
* 具体产品A
*/
class ConcreteProductA implements Product {
@Override
public void show() {
System.out.println("具体产品A");
}
}
/**
* 具体产品B
*/
class ConcreteProductB implements Product {
@Override
public void show() {
System.out.println("具体产品B");
}
}
/**
* 工厂接口
*/
interface Factory {
Product createProduct();
}
/**
* 具体工厂A
*/
class ConcreteFactoryA implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductA();
}
}
/**
* 具体工厂B
*/
class ConcreteFactoryB implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductB();
}
}
/**
* @author ZhouQuan
* @desciption 工厂方法模式(Factory Method Pattern)
* @date 2023/7/23 11:35
*/
public class FactoryMethodPattern {
public static void main(String[] args) {
Factory factoryA = new ConcreteFactoryA();
Factory factoryB = new ConcreteFactoryB();
Product productA = factoryA.createProduct();
Product productB = factoryB.createProduct();
productA.show();
productB.show();
}
}
观察者模式(Observer Pattern)是一种行为型设计模式
,用于定义一对多的依赖关系,使得当一个对象的状态发生变化时,所有依赖于它的对象都能够得到通知并自动更新
Subject(主题):也称为被观察者或可观察者
,它是一个具有状态的对象,当它的状态发生变化时,会通知所有注册过的观察者
Observer(观察者):观察者关注主题的状态变化,在主题发生变化时,接收到通知并进行相应的更新操作
观察者模式的优点在于解耦了主题和观察者
,使得它们可以相互独立地变化,同时也增加了系统的灵活性和扩展性
Subject(主题)接口:定义了添加、删除和通知观察者的方法
ConcreteSubject(具体主题)类:实现了Subject接口,维护了观察者列表,并在状态发生变化时通知观察者
Observer(观察者)接口:定义了接收通知并作出相应更新的方法
ConcreteObserver(具体观察者)类:实现了Observer接口,在接收到主题通知时执行相应的操作
package com.zhouquan.entity.observer;
import java.util.ArrayList;
import java.util.List;
/**
* 主题接口
*/
interface Subject {
/**
* 注册观察者
*/
void registerObserver(Observer observer);
/**
* 移除观察者
*/
void removeObserver(Observer observer);
/**
* 通知所有已注册的观察者
*/
void notifyObservers();
}
/**
* 具体主题类
* 负责维护观察者列表和状态,并在状态发生变化时通知所有注册的观察者
*/
class ConcreteSubject implements Subject {
/**
* 观察者对象列表
*/
private List<Observer> observers = new ArrayList<>();
/**
* 观察者状态
*/
private String state;
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(state);
}
}
/**
* 设置状态,并通知观察者
*
* @param state
*/
public void setState(String state) {
this.state = state;
notifyObservers();
}
}
/**
* 观察者接口
*/
interface Observer {
void update(String state);
}
/**
* 具体观察者类
* 实现了Observer接口,在接收到主题通知时执行相应的操作
*/
class ConcreteObserver implements Observer {
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
@Override
public void update(String state) {
System.out.println(name + "收到更新,新状态为:" + state);
}
}
/**
* @author ZhouQuan
* @description 观察者模式
* @date 2023-07-23 21:00
**/
public class ObserverPattern {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
Observer observer1 = new ConcreteObserver("Observer1");
Observer observer2 = new ConcreteObserver("Observer2");
subject.registerObserver(observer1);
subject.registerObserver(observer2);
subject.setState("State 1");
subject.removeObserver(observer1);
subject.setState("State 2");
}
}
在main方法代码中创建了一个具体主题对象subject
和两个具体观察者对象observer1
和observer2
。首先将两个观察者注册到主题上,然后通过setState()
方法改变主题的状态,观察者会收到通知并更新。之后从主题中移除一个观察者,再次改变主题的状态,只有一个观察者会收到通知。
这样,观察者模式就成功地实现了一对多
的依赖关系,当主题状态发生变化时,所有观察者都能够得到通知并作出相应的更新
策略模式(Strategy Pattern)是一种行为型设计模式
,它定义了一族算法,并将每个算法封装起来,使它们之间可以互相替换,从而使客户端代码与具体算法的实现解耦
策略模式的主要目的是让算法的变化独立于使用算法的客户端
。这样,客户端代码就可以根据需要选择不同的算法,而不需要修改其代码,从而实现了算法的动态切换
Context(环境):环境类,用于持有一个策略对象,并在需要时调用策略对象的方法。它通常代表客户端使用的接口
Strategy(策略):策略接口,定义了算法的公共接口
。具体的策略类实现了策略接口,提供不同的算法实现
。
ConcreteStrategy(具体策略):具体策略类
,实现了策略接口定义的算法。在客户端选择不同的策略时,使用不同的具体策略类
+-------------------+
| Strategy |
+-------------------+
| + doOperation() |
+-------------------+
^
|
| implements
|
+------------------------+ +------------------------+
| ConcreteStrategyA | | ConcreteStrategyB |
+------------------------+ +------------------------+
| + doOperation() | | + doOperation() |
+------------------------+ +------------------------+
^
|
| uses
|
+------------+
| Context |
+------------+
| - strategy |
+------------+
^
|
| depends on
|
+--------------+
| Main |
+--------------+
策略模式的优点在于可以有效地避免使用大量的条件判断语句,将算法的选择和使用与客户端的代码解耦,提高代码的灵活性和可维护性
package com.zhouquan.entity.strategy;
/**
* IP策略接口
*/
interface IpStrategy {
/**
* 校验ip类型
* 判断是IPv4还是IPv6
*
* @param ip ip字符串
* @return string 返回校验结果
*/
String checkIpType(String ip);
}
/**
* 具体策略 IPv4
*/
class IPv4Strategy implements IpStrategy {
@Override
public String checkIpType(String ip) {
return "使用IPv4策略,结果为:" + ip;
}
}
/**
* 具体策略 IPv6
*/
class IPv6Strategy implements IpStrategy {
@Override
public String checkIpType(String ip) {
return "使用IPv6策略,结果为:" + ip;
}
}
/**
* 环境类
*/
class IpContext {
private IpStrategy ipStrategy;
public IpContext(IpStrategy ipStrategy) {
this.ipStrategy = ipStrategy;
}
public String checkIpType(String ipAddress) {
return ipStrategy.checkIpType(ipAddress);
}
}
/**
* @author ZhouQuan
* @description 策略模式(Strategy Pattern)
* @date 2023-07-23 21:27
**/
public class StrategyPattern {
public static void main(String[] args) {
String ipv4Address = "192.168.1.1";
String ipv6Address = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
/**
* 策略模式的主要目的是让算法的变化独立于使用算法的代码
* 通过实例化不同的`Context`对象,并传入不同的具体策略类可以根据需要选择不同的算法,而不
* 需要修改其代码,从而实现了算法的动态切换
*/
IpContext ipv4Context = new IpContext(new IPv4Strategy());
IpContext ipv6Context = new IpContext(new IPv6Strategy());
String ipv4Result = ipv4Context.checkIpType(ipv4Address);
String ipv6Result = ipv6Context.checkIpType(ipv6Address);
System.out.println("IPv4 地址校验:" + ipv4Result);
System.out.println("IPv6 地址校验:" + ipv6Result);
}
}
在上述示例中,我们创建了一个策略接口IpStrategy
,并实现了两种具体策略类IPv4Strategy
和IPv6Strategy
,它们分别执行不同的算法来校验ip类型IpContext
类是环境类,持有一个策略对象,并在需要时调用策略对象的方法
适配器模式(Adapter Pattern)是一种结构型设计模式
,它允许将一个类的接口转换成客户端所期望的另一个接口,从而使原本不兼容的类能够一起工作。适配器模式主要用于解决现有系统之间接口不兼容的问题,同时也能够将已有类和新类进行协调工作
目标接口(Target):客户端期望的接口,它定义了客户端可以调用的方法
适配器(Adapter):适配器是将原有接口转换成目标接口的中间桥梁
。它实现了目标接口,并持有原有接口的引用,将客户端请求转发给原有接口
被适配者(Adaptee):被适配者是已有的类,它的接口和客户端期望的目标接口不兼容
适配器模式的优点在于它可以使不兼容的类能够协调工作,同时也提高了代码的复用性和灵活性
package com.zhouquan.entity.adapter;
/**
* 目标接口
*/
interface MediaPlayer {
/**
* 视频播放方法
*
* @param audioType 视频类型
* @param fileName 文件名
*/
void play(AudioType audioType, String fileName);
}
/**
* 视频类型的枚举类
*/
enum AudioType {
MP4,
VLC
}
/**
* 被适配者:AdvancedMediaPlayer接口
*/
interface AdvancedMediaPlayer {
/**
* vlc格式播放
*
* @param fileName
*/
void playVlc(String fileName);
/**
* mp4格式播放
*
* @param fileName
*/
void playMp4(String fileName);
}
/**
* 具体被适配者:VlcPlayer类
*/
class VlcPlayer implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
System.out.println("播放vlc格式.文件名: " + fileName);
}
@Override
public void playMp4(String fileName) {
// 空实现,不处理
}
}
/**
* 具体被适配者:Mp4Player类
*/
class Mp4Player implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
// 空实现,不处理
}
@Override
public void playMp4(String fileName) {
System.out.println("播放mp4格式.文件名: " + fileName);
}
}
/**
* 适配器类:MediaPlayerAdapter
*/
class MediaPlayerAdapter implements MediaPlayer {
private AdvancedMediaPlayer advancedMediaPlayer;
public MediaPlayerAdapter(AudioType audioType) {
if (audioType == AudioType.VLC) {
advancedMediaPlayer = new VlcPlayer();
} else if (audioType == AudioType.MP4) {
advancedMediaPlayer = new Mp4Player();
}
}
@Override
public void play(AudioType audioType, String fileName) {
if (audioType == AudioType.VLC) {
advancedMediaPlayer.playVlc(fileName);
} else if (audioType == AudioType.MP4) {
advancedMediaPlayer.playMp4(fileName);
}
}
}
/**
* @author ZhouQuan
* @description 适配器模式(Adapter Pattern)
* @date 2023-07-23 22:03
**/
public class AdapterPattern {
public static void main(String[] args) {
MediaPlayer mediaPlayer = new MediaPlayerAdapter(AudioType.MP4);
mediaPlayer.play(AudioType.MP4, "蔡徐坤打篮球.mp4");
}
}
在上述示例中实现了一个简单的适配器模式。MediaPlayer
是目标接口,AdvancedMediaPlayer
是已有的不兼容接口。VlcPlayer
和 Mp4Player
是具体的被适配者。MediaPlayerAdapter
是适配器类,它实现了目标接口,并持有一个 AdvancedMediaPlayer
引用,将客户端请求转发给具体被适配者
装饰器模式(Decorator Pattern)是一种结构型设计模式
,允许你在不改变对象接口的情况下,动态地添加功能到对象上
。它通过创建一个装饰器类来包装原始对象,从而为原始对象添加新的行为。这样可以使代码更加灵活,同时符合开放封闭原则(Open/Closed Principle),即对扩展开放,对修改关闭
Component(抽象构件):定义了一个抽象接口,可以是接口或抽象类,被装饰的对象和装饰器都实现这个接口
ConcreteComponent(具体构件):实现了抽象构件接口,是被装饰的原始对象
Decorator(装饰器):也是抽象类或接口,持有一个对抽象构件的引用,并定义与抽象构件一致的接口
ConcreteDecorator(具体装饰器):继承自装饰器类,实现了装饰器接口,负责在具体构件上添加新的功能
下面用一个简单的 Java 示例来说明装饰器模式的用法。以咖啡店为例,有不同种类的咖啡(具体构件),可以在咖啡中添加额外的调料(具体装饰器)
package com.zhouquan.entity.decorator;
/**
* Component(抽象构件)
*/
interface Coffee {
double getCost();
String getDescription();
}
/**
* ConcreteComponent(具体构件)
*/
class SimpleCoffee implements Coffee {
@Override
public double getCost() {
return 10.0;
}
@Override
public String getDescription() {
return "一杯原始咖啡 ";
}
}
/**
* Decorator(装饰器)
*/
abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee;
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
}
/**
* ConcreteDecorator(具体装饰器)
*/
class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public double getCost() {
return coffee.getCost() + 5.0;
}
@Override
public String getDescription() {
return coffee.getDescription() + ",加牛奶 ";
}
}
/**
* ConcreteDecorator(具体装饰器)
*/
class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public double getCost() {
return coffee.getCost() + 2.0;
}
@Override
public String getDescription() {
return coffee.getDescription() + ",加糖 ";
}
}
/**
* @author ZhouQuan
* @description 装饰器模式(Decorator Pattern)
* @date 2023-07-23 22:33
**/
public class DecoratorPattern {
public static void main(String[] args) {
// 原始咖啡
Coffee simpleCoffee = new SimpleCoffee();
// 一杯原始咖啡 10.0
System.out.println(simpleCoffee.getDescription() + simpleCoffee.getCost());
// 使用装饰器添加调料
Coffee milkCoffee = new MilkDecorator(simpleCoffee);
Coffee sugarMilkCoffee = new SugarDecorator(milkCoffee);
//一杯原始咖啡 ,加牛奶 ,加糖 17.0
System.out.println(sugarMilkCoffee.getDescription() + sugarMilkCoffee.getCost());
}
}
通过装饰器模式可以轻松地给咖啡添加不同的调料,而不需要修改原始的咖啡类。这使得代码更加灵活,并且可以方便地增加新的装饰器类来拓展功能
命令模式(Command Pattern)是一种行为型设计模式
,用于将请求(命令)封装成一个对象,从而允许你使用不同的请求、队列或者日志来参数化其他对象。它可以将请求的发送者(Invoker)和接收者(Receiver)解耦,让发送者不需要知道接收者的具体处理过程,只需通过命令对象来执行请求
Command(命令):声明执行操作的接口,通常包含一个execute
方法。
ConcreteCommand(具体命令):实现Command接口,在execute
方法中封装了具体的操作,同时持有一个接收者对象。
Receiver(接收者):负责执行具体的操作。
Invoker(调用者):负责调用命令对象的execute
方法来执行请求。
假设我们有一个遥控器,上面有一些按钮,每个按钮可以控制不同的电器设备(例如电灯、音响等)
package com.zhouquan;
/**
* Command(命令)
*/
interface Command {
void execute();
}
/**
* ConcreteCommand(具体命令) - 电灯开启命令
*/
class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOn();
}
}
/**
* ConcreteCommand(具体命令) - 电灯关闭命令
*/
class LightOffCommand implements Command {
private Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOff();
}
}
/**
* Receiver(接收者) - 电灯
*/
class Light {
public void turnOn() {
System.out.println("开灯");
}
public void turnOff() {
System.out.println("关灯");
}
}
/**
* Invoker(调用者) - 遥控器
*/
class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}
/**
* @author ZhouQuan
* @description 命令模式(Command Pattern)
* @date 2023-07-23 22:45
**/
public class CommandPattern {
public static void main(String[] args) {
RemoteControl remoteControl = new RemoteControl();
Light light = new Light();
Command lightOnCommand = new LightOnCommand(light);
Command lightOffCommand = new LightOffCommand(light);
remoteControl.setCommand(lightOnCommand);
remoteControl.pressButton();
remoteControl.setCommand(lightOffCommand);
remoteControl.pressButton();
}
}
通过命令模式将请求(控制电灯)封装成了具体的命令对象,使得发送者(遥控器)和接收者(电灯)解耦,从而实现了更灵活和可扩展的控制方式