23种常用设计模式快速入门教程
一、简介
扩展阅读
常用设计模式UML图示
常用23种设计模式Java经典实现(使用常用电商业务订单、购物车,商户,支付,优惠券为例)
JavaScript版本的23种常用设计模式快速入门教程
1.设计模式定义
设计模式是一组有用的解决方案,用于解决特定类型的软件设计问题。它们通常提供了一种抽象出来的方式,来表达应用程序中常见问题的解决方案,从而帮助开发者更有效地解决问题。设计模式的用途是帮助开发者解决软件设计问题,提高开发效率,降低开发成本,提高代码质量和可维护性,以及更好地管理和理解复杂的系统。设计模式的优点是可以提高代码可维护性,减少代码重复,提高开发效率,降低开发成本,提高代码质量和可维护性,以及更好地管理和理解复杂的系统。设计模式的缺点是可能会使代码变得复杂,也可能会过度设计。设计模式的出处是由GoF(Gang of Four)在1995年发表的著作“设计模式:可复用面向对象软件的基础”中提出。
2.设计模式的历史
设计模式可以追溯到1960年代,当时著名的软件工程师和设计师们开始研究如何提高软件开发的效率和可维护性。他们发现,应用一些重复出现的设计模式可以提高软件开发的效率,并极大地改善软件的可读性和可维护性。这些设计模式在1970年代末被称为“永恒的设计模式”,并在1995年由Erich Gamma,Richard Helm, Ralph Johnson和John Vlissides出版。他们的书,称为“设计模式:可复用面向对象软件的基础”,成为设计模式的代表作品,并且引发了关于设计模式的热烈讨论。此外,这本书还为软件开发人员提供了一套可复用的设计模式,以帮助他们更好地理解和实施设计模式
二、设计模式的六大原则
总原则:开闭原则(Open Close Principle)
实体应该对扩展开放,对修改关闭,即软件实体应尽量在不修改原有代码的情况下进行扩展。
应用时注意点是要充分利用抽象类和接口、多态等特性,将可变部分封装起来,提高系统的灵活性和可维护性。
1、单一职责原则
一个类应该只负责一项职责,不要存在多于一个的非本职责原因引起类的变更。简单来说就是类的职责要单一。
应用时注意点是要在设计时尽量把类的职责划分清楚,把功能模块化,提高系统的灵活性和可维护性。
2、里氏替换原则(Liskov Substitution Principle)
里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
历史替换原则中,子类对父类的方法尽量不要重写和重载。因为父类代表了定义好的结构,通过这个规范的接口与外界交互,子类不应该随便破坏它。
应用时注意点是在设计时应让父类和子类之间有一个共同的抽象层,以便子类可以重写父类的抽象方法,并在子类中实现这些抽象方法。
3、依赖倒转原则(Dependence Inversion Principle)
这个是开闭原则的基础,具体内容:面向接口编程,依赖于抽象而不依赖于具体。
应用时注意点是要在设计时以抽象为基础,尽量将细节层次降低,使系统可以更灵活地应对变化。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。
4、接口隔离原则(Interface Segregation Principle)
每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使用多个隔离的接口,比使用单个接口(多个接口方法集合到一个的接口)要好。简单地说使用多个专门的接口,而不使用单一的总接口。
应用时注意点是要把接口进行分解,使用多个小接口,以便更好地实现功能的模块化管理,提高系统的灵活性和可维护性。
5、迪米特法则(最少知道原则)(Demeter Principle)
就是说:一个类对自己依赖的类知道的越少越好。也就是说无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过public方法提供给外部。这样当被依赖的类变化时,才能最小的影响该类。
最少知道原则的另一个表达方式是:只与直接的朋友通信。类之间只要有耦合关系,就叫朋友关系。耦合分为依赖、关联、聚合、组合等。我们称出现为成员变量、方法参数、方法返回值中的类为直接朋友。局部变量、临时变量则不是直接的朋友。我们要求陌生的类不要作为局部变量出现在类中。
应用时注意点是要尽量减少类与类之间的耦合,提高系统的灵活性和可维护性。
6、合成复用原则(Composite Reuse Principle)
指尽量使用组合/聚合的方式,而不是使用继承关系来达到复用的目的。
应用时注意点是要尽量使用组合/聚合的方式,使用继承可能带来的耦合性太强,从而影响系统的灵活性和可维护性。
二、设计模式的分类
总体来说设计模式分为三大类:
创建型设计模式,共五种:工厂方法设计模式、抽象工厂设计模式、单例设计模式、建造者设计模式、原型设计模式。
结构型设计模式,共七种:适配器设计模式、装饰器设计模式、代理设计模式、外观设计模式、桥接设计模式、组合设计模式、享元设计模式。
行为型设计模式,共十一种:策略设计模式、模板方法设计模式、观察者设计模式、迭代子设计模式、责任链设计模式、命令设计模式、备忘录设计模式、状态设计模式、访问者设计模式、中介者设计模式、解释器设计模式。
1. 工厂方法设计模式(Factory Method Pattern)
定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。
它的思想是:在工厂设计模式中,我们创建对象不是直接使用 new 关键字创建,而是将创建对象的行为委托给另一个类,也就是工厂类。把实例化的步骤抽象出来,让子类决定实例化哪一个类。
应用场景:当一个类不知道它所必须创建的对象的类的时候,或者根据不同条件创建不同实例时,可以使用工厂设计模式。
2. 抽象工厂设计模式(Abstract Factory Pattern)
抽象工厂设计模式是一种创建型设计模式,它提供一种方法创建一组相关的产品,而无需指定它们的具体类。
优点:具体产品类的实现细节完全隐藏,不需要关心创建细节;增加新的具体产品类只需要添加一个新的具体产品类和相应的具体工厂类,不会影响已有系统;符合“开放-封闭”原则。
缺点:增加新的产品类比较困难,需要修改抽象工厂和所有的具体工厂类。
3.单例设计模式(Singleton Pattern)
单例设计模式是一种常用的软件设计模式,指的是在一个类里面有一个私有构造函数和一个公开静态方法,这个方法返回对象的唯一实例。
单例设计模式的应用场景是:需要一个只能创建一个实例的类,如 配置类、日志类、计数器类等。
优点:降低内存开销;确保全局唯一;可以控制实例数量。
缺点:不支持继承;不灵活;与单一职责原则冲突。
4.建造者设计模式(Builder Pattern)
建造者设计模式是一种创建型设计模式,它使用抽象的建造者来构建一个复杂的对象。
它的思想是:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
建造者设计模式的应用场景是:当创建一个复杂的对象时,可以使用建造者设计模式来将复杂对象的创建过程与它的表示进行分离,从而使得构建过程更加清晰。
优点:构建和表示分离;良好的封装性;可以构造复杂的对象。
缺点:难以控制产品的组成部分;产品的组成部分太多,可能会出现很多具体的建造者类。
5.原型设计模式
原型设计模式是一种创建型设计模式,它使用一个原型对象来创建新的对象。
它的思想是:通过复制一个已经存在的实例来创建新的实例,而不是通过new关键字来创建新的实例。
原型设计模式的应用场景是:当类的创建开销比较大或者构造函数中需要复杂操作时,原型设计模式可以派上用场。
优点:性能优良;简化创建过程;灵活性高。
缺点:配备克隆方法需要对类的功能进行较大的改造,这是非常有风险的改动;必须实现Cloneable接口;使用原型设计模式时,不能使用final类型。
6.适配器设计模式(Adapter Pattern)
适配器设计模式是一种结构型设计模式,它将一个类的接口转换成客户希望的另一个接口。
它的思想是:将一个类的接口转换成客户希望的另一个接口,从而使原本接口不兼容的类可以一起工作。
适配器设计模式的应用场景是:当你希望使用一个已经存在的类,但是它的接口与你的要求不匹配时,可以使用适配器设计模式来解决这个问题。
优点:提高了类的复用性;增加了类的透明度;灵活性高。
缺点:增加了系统的复杂度;增加系统的理解和设计难度。
7.装饰者设计模式(Decorator Pattern)
装饰者设计模式是一种结构型设计模式,它可以在不改变一个对象的结构的情况下,给对象增加一些新的功能。
它的思想是:将一个类的功能进行加强,但是不改变它的结构。
装饰者设计模式的应用场景是:当你希望给一个类增加新的功能,而且不希望改变它的结构时,可以使用装饰者设计模式。
8.外观设计模式(Facade Pattern)
外观设计模式是一种结构型设计模式,它可以为复杂的子系统提供简单的接口,以便客户端可以方便地访问子系统。
它的思想是为复杂的子系统提供简单的接口,以便客户端可以方便地访问子系统。它的应用了依赖倒置原则。
应用场景是需要为复杂的子系统提供简单接口的场景。
9.桥接设计模式(Bridge Pattern)
桥接设计模式是一种结构型设计模式,它可以将抽象和实现解耦,从而让它们可以独立变化。它应用了接口隔离原则。
应用场景是需要将抽象和实现解耦的场景。
10.代理设计模式(Proxy Pattern)
代理设计模式是一种结构型设计模式,它可以为另一个对象提供一个替身或代理,以控制对这个对象的访问。它应用了迪米特法则。
应用场景是需要为另一个对象提供一个替身或代理的场景。
11.组合设计模式(Composite Pattern)
组合设计模式是一种结构型设计模式,它可以将对象组合成树形结构,从而使客户端可以使用统一的接口来处理单个对象和组合对象。它的原则是合成复用原则。
应用场景是需要将对象组合成树形结构的场景。
12.责任链设计模式(Chain of Responsibility Pattern)
13.命令设计模式(Command Pattern)
14.解释器设计模式(Interpreter Pattern)
15.迭代器设计模式(Iterator Pattern)
16.中介者设计模式(Mediator Pattern)
17.观察者设计模式(Observer Pattern)
18.状态设计模式(State Pattern)
19.策略设计模式(Strategy Pattern)
20.模板方法设计模式(Template Pattern)
21.备忘录设计模式(Memento Pattern)
22.访问者设计模式(Visitor Pattern)
23.享元设计模式(Flyweight Pattern)
三、使用示例
工厂方法设计模式(Factory Method Pattern)
/*
定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。
它的思想是:在工厂设计模式中,我们创建对象不是直接使用 new 关键字创建,而是将创建对象的行为委托给另一个类,也就是工厂类。把实例化的步骤抽象出来,让子类决定实例化哪一个类。
应用场景:当一个类不知道它所必须创建的对象的类的时候,或者根据不同条件创建不同实例时,可以使用工厂设计模式。
*/
/*
简单工厂模式
首先,我们创建一个抽象类 Order
,代表订单,其中包含一个 process()
方法,用于处理订单。然后,我们创建两个具体的订单类 OnlineOrder
和 OfflineOrder
,分别代表在线订单和线下订单,实现 Order
接口,并重写 process()
方法。接下来,我们创建一个工厂类 OrderFactory
,用于根据订单类型创建不同的订单对象。最后,我们使用工厂类创建订单对象,并调用它们的 process()
方法。
*/
//抽象类 Order
abstract class Order {
public abstract void process();
}
//在线订单
class OnlineOrder extends Order {
@Override
public void process() {
System.out.println("Processing online order.");
}
}
//线下订单
class OfflineOrder extends Order {
@Override
public void process() {
System.out.println("Processing offline order.");
}
}
//工厂类 OrderFactory
class OrderFactory {
public static Order createOrder(String type) {
switch (type) {
case "online":
return new OnlineOrder();
case "offline":
return new OfflineOrder();
default:
throw new IllegalArgumentException("Invalid order type: " + type);
}
}
}
//客户端
public class Main {
public static void main(String[] args) {
Order onlineOrder = OrderFactory.createOrder("online");
onlineOrder.process();
Order offlineOrder = OrderFactory.createOrder("offline");
offlineOrder.process();
}
}
抽象工厂设计模式(Abstract Factory Pattern)
/*
抽象工厂设计模式是一种创建型设计模式,它提供一种方法创建一组相关的产品,而无需指定它们的具体类。
*/
/**
* 抽象工厂类
*/
public abstract class AbstractCouponFactory {
/**
* 创建优惠券
*/
public abstract Coupon createCoupon();
/**
* 创建活动
*/
public abstract Promotion createPromotion();
}
/**
* 优惠券类
*/
public abstract class Coupon {
public abstract void useCoupon();
}
/**
* 新用户优惠券
*/
public class NewUserCoupon extends Coupon {
public void useCoupon() {
System.out.println("使用新用户优惠券");
}
}
/**
* 活动类
*/
public abstract class Promotion {
public abstract void startPromotion();
}
/**
* 双十一活动
*/
public class DoubleElevenPromotion extends Promotion {
public void startPromotion() {
System.out.println("开始双十一活动");
}
}
/**
* 母婴类优惠券工厂
*/
public class MotherAndBabyCouponFactory extends AbstractCouponFactory {
public Coupon createCoupon() {
return new NewUserCoupon();
}
public Promotion createPromotion() {
return new DoubleElevenPromotion();
}
}
/**
* 客户端代码
*/
public class Client {
public static void main(String[] args) {
AbstractCouponFactory factory = new MotherAndBabyCouponFactory();
Coupon coupon = factory.createCoupon();
Promotion promotion = factory.createPromotion();
coupon.useCoupon();
promotion.startPromotion();
}
}
//单例设计模式
/*
单例设计模式的思想是将类的实例化控制在一个对象中,一个类只能有一个实例。
它的应用场景主要是在需要全局控制的情况下,例如在电商平台中,订单流程的控制,可以使用单例设计模式。
在多线程环境下,双重检查锁和 volatile 关键字的组合可以保证单例模式的线程安全性和正确性。
*/
public class OrderService {
private OrderService() {
}
if (instance == null) {
synchronized (OrderService.class) {
if (instance == null) {
instance = new OrderService();
}
}
}
return instance;
}
public void placeOrder(Order order) {
//...
}
}
//原型设计模式
/*
原型设计模式的思想是通过复制原型来创建一个新的对象。它的应用场景一般是在创建复杂对象时,可以先复制一个原型来进行一些初始化的工作,
然后在对其进行修改。在电商平台中,可以使用原型设计模式来创建订单对象,避免重复的初始化工作。
*/
public class Order implements Cloneable {
private String orderId;
private List<String> items;
public Order(String orderId, List<String> items) {
this.orderId = orderId;
this.items = items;
}
public Order clone() {
try {
return (Order) super.clone();
}
catch (CloneNotSupportedException e) {
e.printStackTrace(); return null;
}
}
}
//建造者设计模式
/*
建造者设计模式的思想是将一个复杂对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示。它的应用场景一般是在创建复杂对象时,
可以将复杂对象的创建过程进行拆分,使得创建过程更加清晰。在电商平台中,可以使用建造者设计模式来创建订单对象,将复杂的构建过程分解成
几个步骤,从而使得创建过程更加清晰。
*/
public class OrderBuilder {
private String orderId;
private List<String> items;
public OrderBuilder setOrderId(String orderId) {
this.orderId = orderId;
return this;
}
public OrderBuilder setItems(List<String> items) {
this.items = items;
return this;
}
public Order build() {
return new Order(orderId, items);
}
}
//适配器设计模式
/*
适配器模式是一种结构型设计模式,它可以将一个类的接口转换成另一个客户端所期望的接口。在这个模式中,适配器类作为两个不兼容接口之间的桥梁,将一个或多个类的接口转换成客户端期望的接口。下面是一个基于产品的 Java 适配器设计模式的示例代码:
*/
// 目标接口
interface Product {
String getName();
double getPrice();
}
// 适配者类
class LegacyProduct {
private final String title;
private final double cost;
public LegacyProduct(String title, double cost) {
this.title = title;
this.cost = cost;
}
public String getTitle() {
return this.title;
}
public double getCost() {
return this.cost;
}
}
// 适配器类
class ProductAdapter implements Product {
private final LegacyProduct legacyProduct;
public ProductAdapter(LegacyProduct legacyProduct) {
this.legacyProduct = legacyProduct;
}
@Override
public String getName() {
return legacyProduct.getTitle();
}
@Override
public double getPrice() {
return legacyProduct.getCost();
}
}
// 客户端代码
public class AdapterDemo {
public static void main(String[] args) {
LegacyProduct legacyProduct = new LegacyProduct("Product A", 12.34);
Product product = new ProductAdapter(legacyProduct);
System.out.println(product.getName()); // 输出:Product A
System.out.println(product.getPrice()); // 输出:12.34
}
}
//装饰者设计模式
/*
装饰者设计模式的思想是动态地将责任附加到对象上。它的应用场景一般是在原有类不满足客户需求时,可以在不改变原有类的基础上通过
装饰者设计模式来动态地增加新的功能。在电商平台中,可以使用装饰者设计模式来为订单对象增加新的功能,比如折扣功能,而不改变原有的订单对象。
*/
public class OrderDecorator {
private Order order;
public OrderDecorator(Order order) {
this.order = order;
}
public void setDiscount(double discount) {
order.setDiscount(discount);
}
public double getDiscount() {
return order.getDiscount();
}
}
//外观设计模式
/*
外观设计模式的思想是为子系统中的一组接口提供一个一致的界面,从而使子系统更加容易使用。它的应用场景一般是在复杂的系统中,可以使用外观设计模式
来简化系统的接口,从而使系统更加容易使用。在电商平台中,可以使用外观设计模式来简化订单系统的接口,使得订单系统更加容易使用。
*/
public class OrderFacade {
private OrderService orderService;
private OrderBuilder orderBuilder;
private OrderAdapter orderAdapter;
private OrderDecorator orderDecorator;
public OrderFacade() {
orderService = OrderService.getInstance();
orderBuilder = new OrderBuilder();
orderAdapter = new OrderAdapter();
orderDecorator = new OrderDecorator();
}
public void placeOrder(Order order) {
orderService.placeOrder(order);
}
public Order buildOrder(String orderId, List<String> items) {
return orderBuilder.setOrderId(orderId).setItems(items).build();
}
public OrderAdapter getOrderAdapter(Order order) {
return new OrderAdapter(order);
}
public OrderDecorator getOrderDecorator(Order order) {
return new OrderDecorator(order); }
}
//桥接设计模式
/*
桥接设计模式的思想是将抽象和实现分离,使它们可以独立变化。它的应用场景一般是在多个变化维度存在时,可以使用桥接设计模式来将抽象和实现分离,
从而使抽象和实现可以独立变化。在电商平台中,可以使用桥接设计模式来创建购物车对象,将购物车的抽象和实现分离,从而使得购物车的抽象和实
现可以独立变化。
*/
public abstract class Cart {
protected CartImplementor implementor;
public Cart(CartImplementor implementor) {
this.implementor = implementor;
}
public abstract void addItem(String item);
public abstract void removeItem(String item);
public abstract List<String> getItems();
}
//代理设计模式
/*
代理设计模式的思想是为其他对象提供一种代理以控制对这个对象的访问。它的应用场景一般是在访问某个对象时,可以使用代理设计模式来控制对这个对象的访问。
在电商平台中,可以使用代理设计模式来访问购物车对象,从而控制对购物车的访问。
*/
public class CartProxy implements Cart {
private Cart cart;
public CartProxy(Cart cart) {
this.cart = cart;
}
@Override
public void addItem(String item) {
cart.addItem(item);
}
@Override
public void removeItem(String item) {
cart.removeItem(item);
}
@Override
public List<String> getItems() {
return cart.getItems();
}
}
//组合设计模式
/*
组合设计模式的思想是将对象组合成树形结构,以表示部分-整体的层次结构。它的应用场景一般是在树形结构中,可以使用组合设计模式来将对象组合成树形结构,
从而表示部分-整体的层次结构。在电商平台中,可以使用组合设计模式来组织购物车对象,将购物车的对象组合成树形结构,从而表示部分-整体的层次结构。
*/
public class CartComposite extends CartComponent {
private List<CartComponent> components = new ArrayList<>();
@Override
public void add(CartComponent component) {
components.add(component);
}
@Override
public void remove(CartComponent component) {
components.remove(component);
}
@Override
public int getTotalPrice() {
int totalPrice = 0;
for (CartComponent component : components) {
totalPrice += component.getTotalPrice();
}
return totalPrice;
}
}
//责任链设计模式
/*
责任链设计模式的思想是将请求的处理者分开,每个处理者对请求做出响应,从而形成一条链,每个处理者都可以决定是否将请求传递给下一个处理者。
它的应用场景一般是在多种处理方法存在时,可以使用责任链设计模式来将请求的处理者分开,从而使得处理者之间形成一条链。在电商平台中,
可以使用责任链设计模式来处理购物车对象,将购物车的处理者分开,从而使得处理者之间形成一条链。
*/
public abstract class CartHandler {
protected CartHandler nextHandler;
public void setNextHandler(CartHandler nextHandler) {
this.nextHandler = nextHandler;
}
public abstract void handle(Cart cart);
}
//命令设计模式
/*
命令设计模式的思想是将请求封装成一个对象,以便使用不同的请求、队列或者日志来参数化其他对象。它的应用场景一般是在请求不同时,
可以使用命令设计模式来将请求封装成对象,从而使用不同的请求参数化其他对象。在电商平台中,可以使用命令设计模式来执行购物车对象的操作,
将购物车的操作封装成对象,从而使用不同的请求参数化其他对象。
*/
public interface Command {
void execute(Cart cart);
}
public class AddItemCommand implements Command {
private String item;
public AddItemCommand(String item) {
this.item = item;
}
@Override
public void execute(Cart cart) {
cart.addItem(item);
}
}
//解释器设计模式
/*解释器设计模式的思想是为一个特定的上下文定义一个语言,并定义该语言的文法,然后使用该文法来解释语言中的句子。它的应用场景一般是在特定上下文中,可以使用解释器设计模式来定义一种语言,并使用该语言的文法来解释句子。在电商平台中,可以使用解释器设计模式来解释购物车对象的操作,定义一种语言,来描述购物车的操作,然后使用该语言的文法来解释句子,从而完成购物车的操作。
*/
public class CartExpressionParser {
public void parse(String expression, Cart cart) {
String[] tokens = expression.split(" ");
for (String token : tokens) {
if (token.equals("add")) {
String item = tokens[1];
cart.addItem(item);
}
else if (token.equals("remove")) {
String item = tokens[1];
cart.removeItem(item);
}
}
}
}
//迭代器设计模式
/*
迭代器设计模式的思想是提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。它的应用场景一般是在聚合对象中,
可以使用迭代器设计模式来提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。在电商平台中,可以使用迭代器
设计模式来遍历支付对象,提供一种方法顺序访问一个支付对象中的各个元素,而又不暴露该对象的内部表示。
*/
public interface Iterator {
boolean hasNext();
Object next();
}
public class PaymentIterator implements Iterator {
private Payment[] payments;
private int index;
public PaymentIterator(Payment[] payments) {
this.payments = payments;
}
@Override
public boolean hasNext() {
if (index >= payments.length || payments[index] == null) {
return false;
} else {
return true;
}
}
@Override
public Object next() {
Payment payment = payments[index];
index++;
return payment;
}
}
//中介者设计模式
//观察者设计模式
//状态设计模式
//策略设计模式
//模板方法设计模式
备忘录设计模式
//访问者设计模式
/*