浅谈设计模式
如何理解设计模式&理性看待设计模式
理性看待设计模式
首先可以肯定的是,单独的进行设计模式的学习是非常的晦涩难懂的。
我们大概都知道在软件工程中,我们在软件设计中,要考虑各种质量属性(如可修改性、可扩展性、可测试性等),简而言之,就是【容易扩展、容易应对变化】。这实际上是一门软件艺术。
然而,我们在学习设计模式的时候,往往都是只看到了最终的艺术成果,忽略了走向艺术的过程。因为缺少对过程的理解,设计模式的学习难度被大大提升。
事实上,在很多时候,优秀的代码是迭代的出来的,是重构得到的。这一步是学习、理解设计模式的最关键的地方。
设计原则与设计模式的关系
设计原则是“道”,而设计模式是“术”。追其本源又回到了设计原则,希望大家可以在学习设计模式之前,先理解设计原则。
什么是设计模式
设计模式是四人帮(GoF)写的一本叫《Design Patterns》的书中提出来的,这本书里面总结了23种设计模式,也是**「最常用的设计模式」**。
所以设计模式其实并不止23种,只是因为这23种是比较常用的设计模式,它们分别适用于不同的场景,也覆盖了绝大多数软件设计的场景。这23种设计模式只是前人的“经验总结”或者“套路”,有时候经验还是蛮重要的,使用这些经验能够少走很多弯路。
有时候多种设计模式可以互相配合起来使用,或者在现有的23种设计模式之上有一些变化,甚至是总结出一些新的“套路”,用来解决现有23种设计模式不能解决的问题,都是正常的。
设计模式是灵活的,并非一成不变。多种设计模式可以相互配合使用,甚至可以在现有模式的基础上进行创新,以解决新的问题。设计模式的核心在于灵活应用,而不是机械套用。
设计模式类型
创建型模式
创建型模式提供了创建对象的机制,能够提升代码的灵活性和可复用性。常见的创建型模式包括:
- 工厂模式
- 抽象工厂模式
- 单例模式
- 建造者模式
- 原型模式
结构型模式
结构型模式关注如何将对象和类组装成更大的结构,同时保持结构的灵活性和高效性。常见的结构型模式包括:
- 适配器模式
- 桥接模式
- 组合模式
- 装饰模式
- 外观模式
- 享元模式
- 代理模式
行为型模式
行为型模式负责对象之间的高效沟通和职责委派。常见的行为型模式包括:
- 责任链模式
- 命令模式
- 迭代器模式
- 中介者模式
- 备忘录模式
- 观察者模式
- 状态模式
- 策略模式
- 模板方法模式
- 访问者模式
创建型模式
工厂模式
工厂模式是一种创建型设计模式,用于封装对象的创建过程,使代码更具灵活性和可维护性。它通过定义一个创建对象的接口,让子类决定实例化哪个类,从而将对象的创建与使用分离。
简单工厂模式(并非是一种单独的设计模式,而是工厂模式的基础)
简单工厂模式通过一个工厂类根据传入的参数决定创建哪种产品对象。
- 优点:结构简单,易于实现。
- 缺点:不符合开闭原则,新增产品类型需修改工厂类。
package com.yiwyn.factory;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
public class SimpleFactoryDemo {
public static void main(String[] args) {
Shape square = ShapeFactory.getShape("square");
System.out.println(square.draw());
}
/**
* 图形工厂类
*/
static class ShapeFactory {
public static Shape getShape(String shapeType) {
if (shapeType.equalsIgnoreCase("square")) {
return new Square("square");
} else if (shapeType.equalsIgnoreCase("triangle")) {
return new Triangle("triangle");
}
return null;
}
}
/**
* 基本图形
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
static class Shape {
private String name;
public String draw() {
return name;
}
}
/**
* 正方形
*/
@Data
@EqualsAndHashCode(callSuper = true)
static class Square extends Shape {
public Square(String name) {
super(name);
}
}
/**
* 三角形
*/
@Data
@EqualsAndHashCode(callSuper = true)
static class Triangle extends Shape {
public Triangle(String name) {
super(name);
}
}
}
工厂方法模式
工厂方法模式定义一个创建对象的接口,由子类决定实例化哪个类。
- 优点:符合开闭原则,扩展性强。
- 缺点:每增加一个产品类,就需要增加一个对应的工厂类,导致类数量增加。
package com.yiwyn.factory;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
public class FactoryMethodDemo {
public static void main(String[] args) {
/*画正方形*/
IShapeFactory shapeFactory = new SquareFactory();
Shape shape = shapeFactory.createShape();
shape.draw();
/*随机大小的三角形*/
shapeFactory = new TriangleFactory();
shape = shapeFactory.createRandomSizeShape();
shape.draw();
}
/**
* 图形工厂类
*/
interface IShapeFactory {
Shape createShape();
Shape createRandomSizeShape();
}
static class SquareFactory implements IShapeFactory {
@Override
public Shape createShape() {
return new Square("正方形");
}
@Override
public Shape createRandomSizeShape() {
return new Square("随机大小的正方形");
}
}
static class TriangleFactory implements IShapeFactory {
@Override
public Shape createShape() {
return new Triangle("三角形");
}
@Override
public Shape createRandomSizeShape() {
return new Triangle("随机大小的三角形");
}
}
/**
* 基本图形
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
static class Shape {
private String name;
public void draw() {
System.out.println(name);
}
}
/**
* 正方形
*/
@Data
@EqualsAndHashCode(callSuper = true)
static class Square extends Shape {
public Square(String name) {
super(name);
}
}
/**
* 三角形
*/
@Data
@EqualsAndHashCode(callSuper = true)
static class Triangle extends Shape {
public Triangle(String name) {
super(name);
}
}
}
抽象工厂模式
提供一个创建一系列相关或依赖对象的接口,无需指定具体类。
讲到抽象工厂就要提到两个概念:产品等级结构和产品族,
如图所示,
- 优点:能创建多个相关对象,保持一致性。
- 缺点:扩展复杂,新增产品族或产品等级结构需修改抽象工厂接口及其实现类。
package com.yiwyn.factory;
/**
* 抽象工厂demo
*/
public class AbstractFactoryDemo {
public static void main(String[] args) {
DeviceAbstractFactory factory = new AppleDeviceFactory();
Phone phone = factory.createPhone();
Laptap laptap = factory.createLaptap();
phone.call();
laptap.work();
}
/**
* 手机
*/
interface Phone {
void call();
}
/**
* 笔记本
*/
interface Laptap {
void work();
}
/**
* 设备抽象工厂
*/
interface DeviceAbstractFactory {
Phone createPhone();
Laptap createLaptap();
}
static class HuaweiPhone implements Phone {
@Override
public void call() {
System.out.println("华为手机");
}
}
static class IPhone implements Phone {
@Override
public void call() {
System.out.println("苹果手机");
}
}
static class HauweiLaptap implements Laptap {
@Override
public void work() {
System.out.println("华为笔记本工作");
}
}
static class MacBookLaptap implements Laptap {
@Override
public void work() {
System.out.println("苹果笔记本");
}
}
/**
* 华为设备工厂
*/
static class HuaweiDeviceFacotry implements DeviceAbstractFactory {
@Override
public Phone createPhone() {
return new HuaweiPhone();
}
@Override
public Laptap createLaptap() {
return new HauweiLaptap();
}
}
/**
* 苹果设备工厂
*/
static class AppleDeviceFactory implements DeviceAbstractFactory {
@Override
public Phone createPhone() {
return new IPhone();
}
@Override
public Laptap createLaptap() {
return new MacBookLaptap();
}
}
}
建造者模式
建造者模式是一种创建型设计模式,它将复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
核心思想
复杂对象的构建算法"与它的"部件及组装方式"分离,使得构件算法和组装方式可以独立应对变化
package com.yiwyn.builder;
import lombok.Data;
import java.io.Serializable;
public class Builder01Demo {
public static void main(String[] args) {
PetBuilder petBuilder = new PetBuilder();
Director director = new Director(petBuilder);
Pet construct = director.construct("dog");
System.out.println(construct);
}
/**
* 宠物类
*/
@Data
static class Pet implements Serializable {
private static final long serialVersionUID = -2890296999914201365L;
private String petName;
private Integer petAge;
}
/**
* 创建者接口
*/
interface Builder {
Builder buildPetName(String name);
Builder buildPetAge(Integer age);
Pet createPet();
}
/**
* 宠物创建者
*/
static class PetBuilder implements Builder {
private final Pet pet = new Pet();
@Override
public PetBuilder buildPetName(String name) {
pet.setPetName(name);
return this;
}
@Override
public PetBuilder buildPetAge(Integer age) {
pet.setPetAge(age);
return this;
}
@Override
public Pet createPet() {
return pet;
}
}
/**
* 指挥者
*/
static class Director {
private final Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public Pet construct(String type) {
if (type.equals("dog")) {
builder.buildPetName("WatchDog");
builder.buildPetAge(1);
return builder.createPet();
} else if (type.equals("cat")) {
builder.buildPetName("TomCat");
builder.buildPetAge(1);
return builder.createPet();
}
return null;
}
}
}
在上面的案例中,我们发现指挥者Director类似乎可以省略,并且Builder接口中已经完全声明了Pet的存在,所以是否可以考虑省去指挥者呢,答案是可以的,并且我们可以发现很多的项目中都会省略掉这个组件。
简化写法
package com.yiwyn.builder;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
public class BuilderDemo {
public static void main(String[] args) {
User build = User.builder().age(20).build();
System.out.println(build);
}
@Data
@Builder
static class User implements Serializable {
private static final long serialVersionUID = -8151220194407102350L;
private String name;
private Integer age;
}
}
// lombok 编译后
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.yiwyn.builder;
import java.io.Serializable;
public class BuilderDemo {
public BuilderDemo() {
}
public static void main(String[] args) {
User build = BuilderDemo.User.builder().age(20).build();
System.out.println(build);
}
static class User implements Serializable {
private static final long serialVersionUID = -8151220194407102350L;
private String name;
private Integer age;
User(String name, Integer age) {
this.name = name;
this.age = age;
}
public static UserBuilder builder() {
return new UserBuilder();
}
public String getName() {
return this.name;
}
public Integer getAge() {
return this.age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public boolean equals(Object o) {
// ... 太多了 省略
}
protected boolean canEqual(Object other) {
return other instanceof User;
}
public int hashCode() {
int PRIME = 59;
int result = 1;
Object $age = this.getAge();
result = result * 59 + ($age == null ? 43 : $age.hashCode());
Object $name = this.getName();
result = result * 59 + ($name == null ? 43 : $name.hashCode());
return result;
}
public String toString() {
return "BuilderDemo.User(name=" + this.getName() + ", age=" + this.getAge() + ")";
}
public static class UserBuilder {
private String name;
private Integer age;
UserBuilder() {
}
public UserBuilder name(String name) {
this.name = name;
return this;
}
public UserBuilder age(Integer age) {
this.age = age;
return this;
}
public User build() {
return new User(this.name, this.age);
}
public String toString() {
return "BuilderDemo.User.UserBuilder(name=" + this.name + ", age=" + this.age + ")";
}
}
}
}
单例模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一,属于创建型模式。它确保一个类只有一个实例,并提供一个全局访问点来访问该实例。
核心思想
- 确保一个类只有一个实例,并提供一个全局访问点来访问该实例。
常见写法
-
懒汉式,线程不安全
public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } -
懒汉式,线程安全
public class Singleton { private static Singleton instance; private Singleton (){} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } -
饿汉式
public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; } } -
双检锁/双重校验锁(DCL)
public class Singleton { private volatile static Singleton singleton; private Singleton (){} public static Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
原型模式
核心思想
原型模式将克隆过程委派给被克隆的实际对象。 模式为所有支持克隆的对象声明了一个通用接口, 该接口让你能够克隆对象, 同时又无需将代码和对象所属类耦合。 通常情况下, 这样的接口中仅包含一个 克隆方法。
为什么要使用原型模式
- 当对象的创建成本比较高时,涉及大量的计算或者资源消耗。例如:数据库连接、网络请求、IO等初始化流程,使用原型模式可以快速通过复制现有对象从而创建新的对象。
- 代码复用,不需要大量重复的初始化代码,新对象只需要修改差异部分。
- 隔离性,每个克隆的对象都是独立的(PS.正常我们克隆使用深拷贝),修改对象不用担心对之前的对象产生影响
- 避免子类化,某些场景我们通过创建继承关系来达成对象的初始化,这种操作会导致类层次结构变得更加复杂,使用原型模式通过复制来代替继承进而进行功能扩展,可以简化类层次结构。
package com.yiwyn.Creational.prototype;
import lombok.Data;
@Data
public class DroolsEntity implements Cloneable {
private String droolsName;
private String droolsRule;
public DroolsEntity(String droolsName, String droolsRule) {
this.droolsName = droolsName;
this.droolsRule = droolsRule;
}
@Override
public DroolsEntity clone() {
try {
return (DroolsEntity) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
// 使用demo
public static void main(String[] args) {
DroolsEntity droolsEntity = new DroolsEntity("droolsName", "rule1");
System.out.println(droolsEntity.toString());
DroolsEntity clone = droolsEntity.clone();
System.out.println(clone.toString());
}
}
补充
Spring中BeanUtils.copyProperties() 方法本质上也是原型模式的思想的应用,可以快速复制对象属性。
行为型模式
策略模式
策略模式是一种行为型设计模式,它定义了一系列算法或行为,并将它们封装在独立的类中,使得它们可以互相替换。策略模式让算法的变化独立于使用它的客户端,从而提高了代码的灵活性和可维护性。
核心思想
- 将算法或行为抽象为接口:
- 定义一个策略接口,所有具体策略类都实现这个接口。
- 封装具体策略:
- 每个具体策略类实现策略接口,提供具体的算法或行为。
- 客户端使用策略:
- 客户端持有一个策略接口的引用,可以在运行时动态切换具体策略。
- 优点:算法可以独立于客户端变化,易于扩展和维护。
- 缺点:客户端需要了解所有策略类,增加了系统的复杂性。
package com.yiwyn.strategy;
import java.util.HashMap;
import java.util.Map;
public class StrategyDemo {
public static void main(String[] args) {
PayContext payContext = new PayContext();
payContext.pay();
}
/**
* 支付服务Context
*/
static class PayContext {
/**
* 这里payService层直接调用了底层逻辑,若逻辑
*/
public void pay() {
IPayStrategy pay = AppConfig.getPay();
pay.pay();
}
}
/**
* 模拟系统配置
*/
static class AppConfig {
// 读取系统配置
private static final String payChannel = System.getProperty("payChannel", "AliPay");
// 支付信息实现
private static final Map<String, IPayStrategy> payMap = new HashMap<>();
static {
payMap.put("AliPay", new AliIPayStrategy());
payMap.put("WxPay", new WxIPayStrategy());
}
public static IPayStrategy getPay() {
return payMap.get(payChannel);
}
}
/**
* 支付接口
*/
interface IPayStrategy {
void pay();
}
/**
* 阿里支付策略
*/
static class AliIPayStrategy implements IPayStrategy {
@Override
public void pay() {
System.out.println("阿里支付");
}
}
/**
* 微信支付策略
*/
static class WxIPayStrategy implements IPayStrategy {
@Override
public void pay() {
System.out.println("微信支付");
}
}
}
责任链模式
核心思想
责任链模式是一种行为型设计模式,责任链模式也叫职责链模式,为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,所以责任链将请求的发送者和请求的处理者解耦了。
- 每个处理对象(Handler) 决定是否处理请求,或将其传递给链中的下一个对象。
- 请求发送者无需知道具体由哪个对象处理,只需将请求提交给链的起始点即可。
适用场景
- 多个对象可以处理同一请求,但具体由哪个对象处理在运行时决定(如审批流程、异常处理、日志记录等)。
- 希望解耦请求发送者和接收者,避免请求者知道具体的处理逻辑。
- 动态调整处理流程(如新增或修改处理顺序)。
优缺点
✅ 优点
- 降低耦合度:请求发送者无需知道具体由哪个对象处理。
- 灵活扩展:可以动态调整处理链(如新增或移除处理者)。
- 符合单一职责原则:每个处理者只负责自己的逻辑。
❌ 缺点
- 请求可能未被处理(如果链中没有合适的处理者)。
- 性能问题(如果链过长,可能会影响处理速度)。
package com.yiwyn.Behavioral.cor;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
/**
* COR demo
* 责任链模式 demo
*/
public class CorDemo {
public static void main(String[] args) {
// yiwyn要休假
PersonLeaveApply apply = new PersonLeaveApply("Yiwyn", 4);
leaveApply(apply);
}
public static void leaveApply(PersonLeaveApply apply) {
SupervisorsApprover supervisorsApprover = new SupervisorsApprover();
supervisorsApprover.setNextHandle(new ManagerApprover());
supervisorsApprover.approve(apply);
}
// 休假申请
@Data
@AllArgsConstructor
public static class PersonLeaveApply {
// 名字
private String name;
// 休假日期
private int leaveDays;
}
// 审批行为
@Data
@AllArgsConstructor
@NoArgsConstructor
public abstract static class ApproverHandle {
// 下一个节点
private ApproverHandle nextHandle;
// 同意天数
public abstract Integer agreeDay();
// 审批
public void approve(PersonLeaveApply apply) {
// 如果当前节点通过,则进入下一个节点
if (apply.getLeaveDays() <= agreeDay()) {
if (nextHandle != null) {
nextHandle.approve(apply);
}
} else {
throw new RuntimeException(apply.getName() + "请假时间" + apply.getLeaveDays() + "不能被允许! " + this.getClass().getSimpleName() + "审批时间:" + agreeDay());
}
}
}
// 主管审批
@Data
@EqualsAndHashCode(callSuper = true)
@AllArgsConstructor
public static class SupervisorsApprover extends ApproverHandle {
@Override
public Integer agreeDay() {
return 4;
}
}
// 经理审批
@Data
@EqualsAndHashCode(callSuper = true)
@AllArgsConstructor
public static class ManagerApprover extends ApproverHandle {
@Override
public Integer agreeDay() {
return 2;
}
}
}
观察者模式
核心概念
观察者模式是一种行为型设计模式,它定义了对象之间的一种一对多依赖关系,当一个对象(称为Subject,主题/被观察者)的状态发生改变时,所有依赖于它的对象(称为Observers,观察者)都会自动收到通知并更新。
工作流程
- 观察者向主题注册
- 主题状态发生变化
- 主题遍历观察者列表,调用每个观察者的更新方法
- 观察者执行相应的更新操作
核心优点
- 松耦合:主题和观察者之间抽象耦合,彼此不知道具体实现
- 动态关系:可以运行时添加或删除观察者
- 广播通信:一个主题变化可通知多个观察者
- 遵循开闭原则:新增观察者无需修改主题代码
实践场景-发布订阅模式
- 观察者模式是发布-订阅模式的基础:
- 发布-订阅模式可以看作是观察者模式的升级版或分布式版本
- 观察者模式是发布-订阅模式的一种简单实现
- 本质相同:
- 都实现了"一个对象状态变化,多个依赖对象自动更新"的机制
- 都遵循了松耦合的设计原则
package com.yiwyn.Behavioral.observer;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.ArrayList;
import java.util.List;
/**
* 观察者模式demo
*/
public class ObserverDemo {
public static void main(String[] args) {
// 添加观察者信息
GitlabSubject gitlabSubject = new GitlabSubject();
gitlabSubject.registerObserver(new GitlabUser("tomCat"));
gitlabSubject.registerObserver(new GitlabUser("watchDog"));
// 更新操作
gitlabSubject.notifyObservers("更新");
}
// 主题
interface Subject {
// 注册观察者
void registerObserver(Observer observer);
// 移除观察者
void removeObserver(Observer observer);
// 通知观察者
void notifyObservers(String opt);
}
@Data
static class GitlabSubject implements Subject {
// 所有订阅的观察者
private List<Observer> observers = new ArrayList<>();
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
// 移除观察者
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
// 通知到观察者
@Override
public void notifyObservers(String opt) {
for (Observer observer : observers) {
observer.action(opt);
}
}
}
// 抽象观察者
abstract static class Observer {
// 行为
public abstract void action(String opt);
}
// gitlab用户
@Data
@EqualsAndHashCode(callSuper = true)
@AllArgsConstructor
static class GitlabUser extends Observer {
private String username;
@Override
public void action(String opt) {
System.out.println(username + "收到了gitlab仓库进行了操作:" + opt);
}
}
}
命令模式
核心概念
命令模式(Command Pattern)是一种行为型设计模式,其核心思想是将请求封装为一个独立的对象,从而使不同的请求、队列或日志请求参数化其他对象,同时支持可撤销的操作。
Q:如何理解将请求封装为一个独立的对象中的请求呢?
A:这个请求是指具体的一个行为,在代码中我们通常使用interface来声明一些行为,这些接口本质就是一类行为。
例如:Light中on就是开灯请求,在命令模式中就需要对on行为进行进一步抽象
public interface Light {
// 开灯
void on();
// 关灯
void off();
}
主要组成部分
- Command (命令接口):声明执行操作的接口
- ConcreteCommand (具体命令):将一个接收者对象绑定于一个动作,实现执行方法
- Invoker (调用者):要求命令执行请求
- Receiver (接收者):知道如何实施与执行一个请求相关的操作
- Client (客户端):创建一个具体命令对象并设定它的接收者
应用场景
- 需要将请求调用者和请求接收者解耦时
- 需要在不同时间指定、排列和执行请求时
- 需要支持撤销/重做操作时
- 需要将操作记录到日志中或支持事务功能时
命令模式的核心优势
- 解耦调用者和接收者:调用者无需知道接收者的具体实现,只需触发命令即可。
- 支持撤销 / 重做:通过
undo()方法可轻松实现操作回滚(如编辑器的撤销功能)。 - 支持命令队列和批处理:可将多个命令组合成复合命令(宏命令),实现批量执行。
- 支持日志记录:可记录命令执行历史,用于故障恢复(如数据库事务日志)。
代码:
package com.yiwyn.Behavioral.command;
import java.util.Scanner;
public class CommandDemo {
public static void main(String[] args) {
// 命令接收者
BedRoomLight bedRoomLight = new BedRoomLight();
// 命令
LightOnCommand lightOnCommand = new LightOnCommand(bedRoomLight);
LightOffCommand lightOffCommand = new LightOffCommand(bedRoomLight);
LightController controller = new LightController(lightOnCommand, lightOffCommand);
Scanner console = new Scanner(System.in);
while (true) {
String command = console.next();
switch (command) {
case "on":
controller.pressLightOn();
break;
case "off":
controller.pressLightOff();
break;
case "undo":
controller.pressUndo();
break;
case "exit":
return;
}
System.out.println("灯的状态:" + bedRoomLight.open);
}
}
/**
* 灯接口
*/
public abstract static class Light {
// 开灯
abstract void on();
// 关灯
abstract void off();
// 状态
Boolean open;
}
/**
* 卧室灯
*/
public static class BedRoomLight extends Light {
@Override
public void on() {
System.out.println("BedRoom on");
this.open = true;
}
@Override
public void off() {
System.out.println("BedRoom off");
this.open = false;
}
}
/**
* 定义“请求” - 命令
*/
public interface Command {
// 执行
void execute();
// 撤销
void undo();
}
/**
* 定义“请求” - 开灯命令
*/
public static class LightOnCommand implements Command {
private final Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
@Override
public void undo() {
light.off();
}
}
/**
* 定义“请求” - 关灯命令
*/
public static class LightOffCommand implements Command {
private final Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.off();
}
@Override
public void undo() {
light.on();
}
}
/**
* 遥控器 有三个按钮:【开】【关】【撤销】
*/
public static class LightController {
private final LightOnCommand lightOnCommand;
private final LightOffCommand lightOffCommand;
private Command command;
public LightController(LightOnCommand lightOnCommand, LightOffCommand lightOffCommand) {
this.lightOnCommand = lightOnCommand;
this.lightOffCommand = lightOffCommand;
}
// 开灯
public void pressLightOn() {
this.lightOnCommand.execute();
command = lightOnCommand;
}
// 关灯
public void pressLightOff() {
this.lightOffCommand.execute();
command = lightOffCommand;
}
// 撤销
public void pressUndo() {
this.command.undo();
}
}
}
小tips:我们手机上的红外功能,操作多品牌空调就是命令模式的思想。
模版方法
核心概念
在一个方法中定义一个算法的骨架,而将一些步骤的实现延迟到子类中,使得子类可以在不改变一个算法的结构前提下即可重定义该算法的某些特定步骤
核心优点
- 提高代码复用性,将相同代码放在父类中
- 允许子类重写算法的特定部分而不改变算法结构
- 实现了反向控制结构,符合好莱坞原则(“不要调用我们,我们会调用你”)
代码示例:
abstract class AbstractClass {
// 模板方法,定义算法骨架
public final void templateMethod() {
primitiveOperation1();
primitiveOperation2();
hook();
}
// 基本方法,由子类实现
protected abstract void primitiveOperation1();
protected abstract void primitiveOperation2();
// 钩子方法,可选重写
protected void hook() { }
}
结构型模式
适配器模式
适配器模式是一种结构型设计模式,它允许不兼容的接口之间进行协作。适配器模式通过将一个类的接口转换成客户端期望的另一个接口,使得原本由于接口不兼容而无法一起工作的类可以协同工作。
核心思想
- 接口转换:
- 适配器模式通过一个适配器类,将目标接口转换为客户端期望的接口。
- 解耦:
- 客户端与目标类之间解耦,客户端只需要依赖目标接口,而无需关心具体实现。
- 复用:
- 适配器模式可以复用现有的类,而无需修改其代码。
package com.yiwyn.adapter;
public class Main {
public static void main(String[] args) {
Mobile mobile = new Mobile();
Power220V power220V = new Power220V();
Power5V power5V = new PowerAdapter(power220V);
mobile.inputPower(power5V);
}
/**
* 手机
*/
static class Mobile {
public void inputPower(Power5V power5V) {
int i = power5V.provider5VPower();
System.out.println("充电电压=" + i);
}
}
/**
* 5v接口
*/
public interface Power5V {
int provider5VPower();
}
/**
* 220v接口
*/
static class Power220V {
public int provider220VPower() {
return 220;
}
}
// 电源适配
static class PowerAdapter implements Power5V {
private final Power220V power220V;
public PowerAdapter(Power220V power220V) {
this.power220V = power220V;
}
@Override
public int provider5VPower() {
int v220 = this.power220V.provider220VPower();
return v220 - 215;
}
}
}
门面模式
核心思想
为复杂的子系统提供一个统一的入口,隐藏系统的复杂性,简化客户端与子系统之间的交互。
优点
- 简化客户端与复杂系统的交互
- 减少客户端对子系统的直接依赖
- 提高系统的安全性和易用性
package com.yiwyn.Structural.Facade;
import lombok.Data;
/**
* 门面模式 demo
*/
public class FacadeDemo {
public static void main(String[] args) {
SmartHomeFacade facade = new SmartHomeFacade();
facade.cdHome();
facade.exitHome();
}
@Data
static class SmartHomeFacade {
private LightSystem lightSystem = new LightSystem();
private AirSystem airSystem = new AirSystem();
private AudioSystem audioSystem = new AudioSystem();
public void cdHome() {
lightSystem.turnOn();
airSystem.turnOn();
audioSystem.turnOn();
}
public void exitHome() {
lightSystem.turnOff();
airSystem.turnOff();
audioSystem.turnOff();
}
}
static class LightSystem {
public void turnOn() {
System.out.println("灯光打开");
}
public void turnOff() {
System.out.println("灯光闭关");
}
}
static class AirSystem {
public void turnOn() {
System.out.println("空调打开");
}
public void turnOff() {
System.out.println("空调闭关");
}
}
static class AudioSystem {
public void turnOn() {
System.out.println("声音打开");
}
public void turnOff() {
System.out.println("声音闭关");
}
}
}
装饰器模式
装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许你动态地向一个对象添加额外的功能,同时又不改变其结构。这种模式通过创建包装对象来实现功能的扩展,是继承关系的一个灵活替代方案。
核心概念
- 不改变原有对象:装饰器模式通过包装对象来扩展功能,而不是通过继承
- 动态添加功能:可以在运行时添加或删除功能
- 遵循开闭原则:对扩展开放,对修改关闭
- 多层装饰:可以嵌套多个装饰器来组合多个功能
Demo:
package com.yiwyn.Structural.Decorator;
import lombok.AllArgsConstructor;
/**
* 装饰器模式
*/
public class DecoratorDemo {
public static void main(String[] args) {
Text text = new PlainText("Hello World");
Text decoratorText = new ItalicDecorator(new BoldDecorator(text));
System.out.println(decoratorText.formatText());
}
// 文本
interface Text {
String formatText();
}
// 简单文本
@AllArgsConstructor
static class PlainText implements Text {
private String text;
@Override
public String formatText() {
return text;
}
}
// BoldDecorator 加粗
@AllArgsConstructor
static class BoldDecorator implements Text {
private Text text;
@Override
public String formatText() {
return "<b>" + text.formatText() + "</b>";
}
}
// ItalicDcorator
@AllArgsConstructor
static class ItalicDecorator implements Text {
private Text text;
@Override
public String formatText() {
return "<i>" + text.formatText() + "</i>";
}
}
}
代理模式
代理模式是一种结构型设计模式,它通过创建一个代理对象来控制对原始对象的访问。代理对象作为原始对象的替代品,可以在访问原始对象前后添加额外的逻辑,而客户端无需知道它操作的是代理还是真实对象。
核心思想
- 访问控制:控制对原始对象的访问权限
- 功能增强:在不修改原始对象的情况下添加额外功能
- 间接访问:客户端通过代理间接操作原始对象
package com.yiwyn.Structural.proxy;
/**
* 代理模式demo
*/
public class ProxyDemo {
public static void main(String[] args) {
IUserService userService = new UserProxy();
userService.saveUser();
}
interface IUserService {
void saveUser();
}
static class UserService implements IUserService {
@Override
public void saveUser() {
System.out.println("保存用户信息!");
}
}
static class UserProxy implements IUserService {
private IUserService userService;
public UserProxy() {
this.userService = new UserService();
}
@Override
public void saveUser() {
System.out.println(UserProxy.class.getSimpleName() + "开启事务");
try {
userService.saveUser();
} catch (Exception e) {
System.out.println("事务回滚");
throw new RuntimeException(e);
}
System.out.println(UserProxy.class.getSimpleName() + "事务提交");
}
}
}
桥接模式
核心思想
桥接模式的核心是 “将抽象部分与实现部分分离,使它们可以独立变化”,通过 “组合” 而非 “继承” 建立两者的联系(即通过一个 “桥” 连接抽象层和实现层)
核心优点
解耦抽象与现实,抽象层和实现层不再通过继承绑定,而是通过组合关联,一方的修改不会强制另一方变动
避免类爆炸问题,若使用继承来实现所有可能的变化,数量是倍数增长
例: 车身颜色 & 新旧,以下案例可以发现, 2个纬度,继承2x3=6个类,而组合只需要5个类,并且随着纬度的升高,桥接模式的优势愈加明显。
继承: 红色的新车、红色的二手车、蓝色的新车、蓝色的二手车、白色的二手车、白色的新车
组合:红色、白色、蓝色 & 新车、二手车
符合组合/聚合原则,桥接模式用 “组合(抽象部分持有实现部分的引用)” 替代 “继承”,既实现了抽象与实现的解耦,又避免了类爆炸,这正是组合 / 聚合原则的核心价值
提高扩展性,抽象层和实现层可以独立扩展新功能,添加新的特征不回影响原有代码
package com.yiwyn.Structural.bridge;
/**
* 桥接模式demo
*/
public class BridgeDemo {
/**
* 构建不同的二手车
*/
public static void main(String[] args) {
RedColor redColor = new RedColor();
OneCar oneCar = new OneCar(redColor);
oneCar.showCarInfo();
BuleColor buleColor = new BuleColor();
TwoCar twoCar = new TwoCar(buleColor);
twoCar.showCarInfo();
}
/**
* 车辆
*/
public static abstract class Car {
private final Color color;
public Car(Color color) {
this.color = color;
}
protected String info() {
return "车";
}
public void showCarInfo() {
System.out.println(color.getColor() + this.info());
}
}
/**
* 颜色
*/
public static abstract class Color {
public Color() {
}
public String getColor() {
return "颜色";
}
}
// -------------------------- 使用 --------------------------------------
/**
* 二手车
*/
public static class TwoCar extends Car {
public TwoCar(Color color) {
super(color);
}
@Override
protected String info() {
return "二手车";
}
}
/**
* 一手车
*/
public static class OneCar extends Car {
public OneCar(Color color) {
super(color);
}
@Override
protected String info() {
return "一手车";
}
}
/**
* 颜色实例-红色
*/
public static class RedColor extends Color {
@Override
public String getColor() {
return "寰宇红";
}
}
/**
* 颜色实例-蓝色
*/
public static class BuleColor extends Color {
@Override
public String getColor() {
return "星际蓝";
}
}
}