设计模式
六种设计原则
- 单一职责原则
组件或模块在封装过程中要保证功能的完善,尽量不要侵入其他的组件,使得组件间关联关系松散 - 里氏替换原则
多用组合,少用继承。继承的使用场景是两个类存在is a
的关系,单纯的为了复用某个逻辑片段,应该避免使用继承,继承使两个类建立了强关联,且java是单继承
组合可以让两个类的关系相对松散 - 依赖倒置原则
应用层如果要使用基础层的内容,应该依赖于基础层的抽象,而不是直接依赖于基础层
所谓的倒置,其实是基础层的倒置,将基础层抽象出接口,基础层是低,接口层是高 - 接口隔离原则
与单一职责类似,接口隔离说的是,接口的抽象应该尽可能是精准的描述事物的特性 - 迪米特原则/最少知道原则
要完成一个功能,应该尽可能少的暴露接口,用最少的接口,完成这个功能,是封装性的体现 - 开闭原则
开闭原则,对修改关闭,对扩展开放
这个原则具体的体现是,尽可能去操作接口,而不是操作具体的类
创建型设计模式
-
单例设计模式
目的:确保对象的唯一性
实现方式:
1.1 饿汉式点击查看代码
/** * @description 单例模式——饿汉式(线程安全,调用效率高,但是不能延时加载) */ public class SingletonDemo1 { private static SingletonDemo1 instance = new SingletonDemo1(); private SingletonDemo1(){} public static SingletonDemo1 getInstance(){ return instance; } }
1.2 懒汉式
点击查看代码
/** * @description 单例模式——懒汉式(线程安全,调用效率不高,但是能延时加载) */ public class SingletonDemo2 { // 类初始化时,不初始化这个对象(延时加载,真正用的时候再创建) private static SingletonDemo2 instance; // 构造器私有化 private SingletonDemo2(){} // 方法同步,调用效率低 public static synchronized SingletonDemo2 getInstance() { if(instance == null) { instance = new SingletonDemo2(); } return instance; } }
1.3 双重校验-懒汉式扩展
点击查看代码
/** * @description 单例模式——双重校验 */ public class SingletonDemo3 { private volatile static SingletonDemo3 singletonDemo3; private SingletonDemo3() {} public static SingletonDemo3 newInstance() { if (singletonDemo3 == null) { synchronized (SingletonDemo3.class) { if (singletonDemo3 == null) { singletonDemo3 = new SingletonDemo3(); } } } return singletonDemo3; } }
- 原型模式
目的:原型模式用于创建一个与源对象相同的对象,为什么不使用new关键字创建对象呢,是因为该对象的创建可能是复杂的,需要大量的准备工作,甚至需要调用其他的网络接口来完成对象的构建
现在我们要获取一个新的对象,可以不使用new关键字,而是使用原型模式,从内存中,直接获取该对象的值信息,来完成对象的创建。
java使用clone方法来完成该动作,不会调用构造器
谈谈深拷贝和浅拷贝
深拷贝和浅拷贝的区别是如何处理目标对象中的引用类型成员
浅拷贝只拷贝目标对象中的基本数据类型,对于引用数据类型,浅拷贝的处理方式是创建一个引用变量,指向源对象的引用成员指向的内存
深拷贝不止是拷贝了基础类型的成员,引用类型的成员,也会被递归拷贝,复制出全新的对象
对于java而言, 浅拷贝实现Cloneable接口、重写clone方法即可,如果需要深拷贝,需要在clone方法返回对象的时候,自行为引用类型的对象赋值
点击查看代码
/**
* @description 原型模式(浅拷贝&深拷贝)
*/
@Data
public class Prototype1 implements Cloneable {
private String name;
private List<String> arrayList = new ArrayList<>();
public static void main(String[] args) {
Prototype1 prototype1 = new Prototype1();
prototype1.setName("original object");
prototype1.setValue("original object");
Prototype1 clonePrototype1 = prototype1.clone();
clonePrototype1.setName("clone object");
/** 发现添加了执行了clone对象的setValue之后,也修改了prototype1中的arrayList中数据 */
clonePrototype1.setValue("clone object");
System.out.println("prototype1=" + prototype1);
System.out.println("clonePrototype1=" + clonePrototype1);
}
public void setValue(String value) {
this.arrayList.add(value);
}
public List<String> getValue() {
return this.arrayList;
}
/**
* 浅拷贝
* @return
*/
@Override
protected Prototype1 clone() {
try {
return (Prototype1) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
/**
* 深拷贝
* @return
*/
// @Override
// protected Prototype1 clone() {
// Prototype1 prototype1 = null;
// try {
// prototype1 = (Prototype1) super.clone();
// prototype1.setArrayList(new ArrayList<>(prototype1.getArrayList()));
// } catch (CloneNotSupportedException e) {
// e.printStackTrace();
// }
// return prototype1;
// }
}
- 工厂设计模式
3.1 简单工厂
将不同的入参,传入工厂的静态方法,工厂会返回不同的对象,对象的创建是在工厂中完成的
举例:存在某个食品加共工厂,制作大饼,简单工厂就是用户告诉工厂,我要什么类型的大饼,工厂就会给你生产出来,假设,我要酱香饼、鸡蛋饼,工厂会根据你提供的类型生产不同的大饼
产品维度:酱香饼、鸡蛋饼
点击查看代码
// 抽象产品
interface Product {
void use();
}
// 具体产品A
class ConcreteProductA implements Product {
public void use() {
System.out.println("Using ConcreteProductA");
}
}
// 具体产品B
class ConcreteProductB implements Product {
public void use() {
System.out.println("Using ConcreteProductB");
}
}
// 简单工厂
class SimpleFactory {
public static Product createProduct(String type) {
if (type.equals("A")) {
return new ConcreteProductA();
} else if (type.equals("B")) {
return new ConcreteProductB();
}
return null;
}
}
// 测试
public class Main {
public static void main(String[] args) {
Product productA = SimpleFactory.createProduct("A");
productA.use();
Product productB = SimpleFactory.createProduct("B");
productB.use();
}
}
点击查看代码
// 抽象产品
interface Product {
void use();
}
// 具体产品A
class ConcreteProductA implements Product {
public void use() {
System.out.println("Using ConcreteProductA");
}
}
// 具体产品B
class ConcreteProductB implements Product {
public void use() {
System.out.println("Using ConcreteProductB");
}
}
// 抽象工厂
interface Factory {
Product createProduct();
}
// 具体工厂A
class ConcreteFactoryA implements Factory {
public Product createProduct() {
return new ConcreteProductA();
}
}
// 具体工厂B
class ConcreteFactoryB implements Factory {
public Product createProduct() {
return new ConcreteProductB();
}
}
// 测试
public class Main {
public static void main(String[] args) {
Factory factoryA = new ConcreteFactoryA();
Product productA = factoryA.createProduct();
productA.use();
Factory factoryB = new ConcreteFactoryB();
Product productB = factoryB.createProduct();
productB.use();
}
}
3.3 抽象工厂
前面的两个案例是从单个产品的角度分析,现在我们引入其他的产品,假设我的食品公司不只是做饼,我还要做饮料,那么现在复杂度又提升了,有了更多的产品了,其实我们可以继续沿用3.2工厂方法的思想,
就是说产品的创建还是放在工厂的实现类中创建,只不过是这个工厂可以创建多个产品,今次而已,其实抽象工厂,本身就是工厂方法模式的扩展,抽象工厂注重产品族的概念。不同的工厂可以随意生产不同的产品,相当灵活
点击查看代码
// 抽象产品A
interface ProductA {
void use();
}
// 具体产品A1
class ConcreteProductA1 implements ProductA {
public void use() {
System.out.println("Using ConcreteProductA1");
}
}
// 具体产品A2
class ConcreteProductA2 implements ProductA {
public void use() {
System.out.println("Using ConcreteProductA2");
}
}
// 抽象产品B
interface ProductB {
void eat();
}
// 具体产品B1
class ConcreteProductB1 implements ProductB {
public void eat() {
System.out.println("Eating ConcreteProductB1");
}
}
// 具体产品B2
class ConcreteProductB2 implements ProductB {
public void eat() {
System.out.println("Eating ConcreteProductB2");
}
}
// 抽象工厂
interface AbstractFactory {
ProductA createProductA();
ProductB createProductB();
}
// 具体工厂1
class ConcreteFactory1 implements AbstractFactory {
public ProductA createProductA() {
return new ConcreteProductA1();
}
public ProductB createProductB() {
return new ConcreteProductB1();
}
}
// 具体工厂2
class ConcreteFactory2 implements AbstractFactory {
public ProductA createProductA() {
return new ConcreteProductA2();
}
public ProductB createProductB() {
return new ConcreteProductB2();
}
}
// 客户端代码
public class Main {
public static void main(String[] args) {
// 使用工厂1:创建ConcreteProductA1和ConcreteProductB1的组合
AbstractFactory factory1 = new ConcreteFactory1();
ProductA productA1 = factory1.createProductA();
ProductB productB1 = factory1.createProductB();
productA1.use();
productB1.eat();
// 使用工厂2:创建ConcreteProductA2和ConcreteProductB2的组合
AbstractFactory factory2 = new ConcreteFactory2();
ProductA productA2 = factory2.createProductA();
ProductB productB2 = factory2.createProductB();
productA2.use();
productB2.eat();
}
}
3.4 建造者/生成器模式(builder)
目的:用于构建复杂对象,对象的构建的过程、参数都可选,可以支持链式编程
对比工厂模式:工厂模式常用于构建简单对象,不需要知道对象创建的细节,而构建者模式的特点是需要你知道对象的参数信息,更加灵活的创建对象
点击查看代码
/**
* 产品类(Computer)
* 表示要构建的复杂对象
* 提供链式的 setter 方法,每个方法返回当前对象 (this)
*/
class Computer {
private String CPU;
private String RAM;
private String storage;
private String graphicsCard;
// Setters with chaining
public Computer setCPU(String CPU) {
this.CPU = CPU;
return this;
}
public Computer setRAM(String RAM) {
this.RAM = RAM;
return this;
}
public Computer setStorage(String storage) {
this.storage = storage;
return this;
}
public Computer setGraphicsCard(String graphicsCard) {
this.graphicsCard = graphicsCard;
return this;
}
@Override
public String toString() {
return "Computer with CPU: " + CPU + ", RAM: " + RAM + ", Storage: " + storage + ", Graphics Card: " + graphicsCard;
}
}
/**
* 建造者类(ComputerBuilder)
* 用于逐步构建 Computer 对象
* 每个方法调用设置 Computer 对象的一个属性,并返回 ComputerBuilder 自身,以便于链式调用
*/
class ComputerBuilder {
private Computer computer;
public ComputerBuilder() {
this.computer = new Computer();
}
public ComputerBuilder buildCPU(String CPU) {
computer.setCPU(CPU);
return this;
}
public ComputerBuilder buildRAM(String RAM) {
computer.setRAM(RAM);
return this;
}
public ComputerBuilder buildStorage(String storage) {
computer.setStorage(storage);
return this;
}
public ComputerBuilder buildGraphicsCard(String graphicsCard) {
computer.setGraphicsCard(graphicsCard);
return this;
}
public Computer build() {
return this.computer;
}
}
/**
* 客户端代码,通过 ComputerBuilder 创建 Computer 对象
* 链式调用各个构建方法,并最终调用 build() 方法生成完整的 Computer 对象
*/
public class Main {
public static void main(String[] args) {
ComputerBuilder builder = new ComputerBuilder();
Computer computer = builder
.buildCPU("Intel i9")
.buildRAM("32GB")
.buildStorage("1TB SSD")
.buildGraphicsCard("NVIDIA RTX 3080")
.build();
System.out.println(computer);
// 输出: Computer with CPU: Intel i9, RAM: 32GB, Storage: 1TB SSD, Graphics Card: NVIDIA RTX 3080
}
}
结构型设计模式
- 代理设计模式
概述:代理模式为增强目标对象提供了一个替身或占位符,来控制目标对象的调用
目的:
功能增强:不改变类原有功能的前提下,对类中的功能,进行横切性的增强
访问控制:
分类:静态代理、动态代理
1.1 静态代理
概述:静态代理是需要研发人员手动创建目标类的代理,相当于创建了新的类,本质就是通过组合的方式,,每个目标类如果都有不同的代理,就会派生出很多的类,冗余、灵活性查
点击查看代码
// 定义接口
public interface Subject {
void request();
}
// 目标类,实现接口
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
// 代理类,实现接口,并持有目标类的引用
public class ProxySubject implements Subject {
private RealSubject realSubject;
public ProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void request() {
// 在调用目标类方法前后添加额外的逻辑
System.out.println("Proxy: Before request");
realSubject.request();
System.out.println("Proxy: After request");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建目标对象
RealSubject realSubject = new RealSubject();
// 创建代理对象,传入目标对象
ProxySubject proxySubject = new ProxySubject(realSubject);
// 通过代理对象调用方法
proxySubject.request();
}
}
-
加载字节码到方法区:
+----------------------+
| 堆区 |
| +-----------------+ |
| | 字节数组 (byte[])|---+
| +-----------------+ |
+----------------------| |
v
+----------------------|------------------------+
| v |
| 方法区 (元空间) |
| +-----------------------------------------+ |
| | Class 对象(代理类的字节码) |<--- 代理类的字节码被加载到方法区
| +-----------------------------------------+ |
+----------------------------------------------+ -
创建代理实例:
+----------------------+
| 堆区 |
| +-----------------+ |
| | 代理类实例对象 |<----- 通过反射机制创建代理类实例
| +-----------------+ |
+----------------------+
点击查看代码
// 1 定义接口(Subject)
public interface Subject {
void request();
}
// 2 实现目标对象(RealSubject)
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
// 3 创建代理处理器(ProxyHandler)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ProxyHandler implements InvocationHandler {
private final Object target;
public ProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Proxy: Before request");
Object result = method.invoke(target, args);
System.out.println("Proxy: After request");
return result;
}
}
// 4 生成代理对象(Client)
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
// 创建实际对象
RealSubject realSubject = new RealSubject();
// 创建代理处理器,将实际对象传递给它
ProxyHandler handler = new ProxyHandler(realSubject);
// 生成代理对象
Subject proxyInstance = (Subject) Proxy.newProxyInstance(
realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
handler
);
// 调用代理对象的方法
proxyInstance.request();
}
}
1.2.2 cglib动态代理
概述:基于继承的代理对象
原理:cglib的代理对象创建原理,就是拿到目标类字节码信息,写一个继承于目标类的子类,然后修改字节码,用新的增强方法,替换原有的目标类的目标方法
点击查看代码
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// 目标类
class RealSubject {
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
// MethodInterceptor 实现类
class ProxyHandler implements MethodInterceptor {
private Object target;
public ProxyHandler(Object target) {
this.target = target;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Proxy: Before request");
// 调用目标类的方法
Object result = proxy.invokeSuper(obj, args);
System.out.println("Proxy: After request");
return result;
}
}
public class Client {
public static void main(String[] args) {
// 创建目标对象
RealSubject realSubject = new RealSubject();
// 创建 Enhancer 对象
Enhancer enhancer = new Enhancer();
// 设置目标类
enhancer.setSuperclass(RealSubject.class);
// 设置回调方法
enhancer.setCallback(new ProxyHandler(realSubject));
// 创建代理对象
RealSubject proxy = (RealSubject) enhancer.create();
// 调用代理对象的方法
proxy.request();
}
}
1.2.3 jdk动态代理和cglib动态代理总结
其实这两个代理对象的创建,都是动态生成字节码,这也是动态的由来,两者的却别是,代理对象的字节码信息的构建过程不同,仅此而已
使用要求上,jdk动态代理要求要多一点,需要目标类实现接口,然后基于反射,可能会慢一些
而cglib性能稍好,但不能继承final的类
- 适配器模式
概述:功能提供者提供的内容,不能被功能使用者直接使用,也就是接口不兼容问题,举例,厂商提供某个接口A,但是客户需要使用接口B,问题产生了,解决方式就是建一个适配器,适配器实现接口B,这样保证适配器可以被客户使用,并且适配器持有接口A的引用,在适配器对接口B的实现方法中,调用接口A的内容,问题解决了。这也是开闭原则的体现,我们应该尽量避免修改代码。
点击查看代码
// 提供的接口
public interface OldLogger {
void logMessage(String message);
}
// 期望的接口
public interface NewLogger {
void log(String message);
}
// 适配器
public class LoggerAdapter implements NewLogger {
private OldLogger oldLogger;
public LoggerAdapter(OldLogger oldLogger) {
this.oldLogger = oldLogger;
}
@Override
public void log(String message) {
oldLogger.logMessage(message);
}
}
- 桥接模式
概述:当不同维度的内容进行组合,就会产生很多的可能性,会造成类爆炸,为什么会类爆炸,解释下,假设我现在要做一个绘图方法,图形方面有圆形,举行,菱形,颜色方面有黑白灰三色,如果不抽象,我需要有3*3=9个类完成所有的排列组合,也就是所谓类爆炸。桥接模式就是在抽象中使用抽象,最大程度上保留了变化,优点就是灵活。
举例:农场有很多的动物,描述用java描述动物们吃饭这个事情
点击查看代码
// 食物接口
interface Food {
String getName();
}
// 具体食物实现类
class Grass implements Food {
public String getName() {
return "Grass";
}
}
class Meat implements Food {
public String getName() {
return "Meat";
}
}
// 动物抽象类
abstract class Animal {
abstract void eat(Food food);
}
// 具体动物类
class Cow extends Animal {
public void eat(Food food) {
System.out.println("Cow is eating " + food.getName());
}
}
class Wolf extends Animal {
public void eat(Food food) {
System.out.println("Wolf is eating " + food.getName());
}
}
public class Farm {
public static void main(String[] args) {
// 创建不同的食物
Food grass = new Grass();
Food meat = new Meat();
// 创建不同的动物
Animal cow = new Cow();
Animal wolf = new Wolf();
// 动物吃不同的食物
cow.eat(grass); // 输出: Cow is eating Grass
wolf.eat(meat); // 输出: Wolf is eating Meat
// 尝试让动物吃不合适的食物
cow.eat(meat); // 输出: Cow is eating Meat (不现实,但示例代码中可行)
wolf.eat(grass); // 输出: Wolf is eating Grass (不现实,但示例代码中可行)
}
}
- 装饰器模式
概述:在不修改目标类代码的情况下,对目标类进行增强,而且这个增强可以堆叠,或者叫做装饰者链,也就是说增强的装饰类可以再次被其他装饰器增强
点击查看代码
// 1 定义咖啡接口和基本咖啡类
// 咖啡接口
interface Coffee {
String getDescription();
double getCost();
}
// 基本咖啡类
class SimpleCoffee implements Coffee {
public String getDescription() {
return "Simple Coffee";
}
public double getCost() {
return 1.0;
}
}
// 2 创建装饰者抽象类
// 装饰者抽象类
abstract class CoffeeDecorator implements Coffee {
protected Coffee decoratedCoffee;
public CoffeeDecorator(Coffee coffee) {
this.decoratedCoffee = coffee;
}
public String getDescription() {
return decoratedCoffee.getDescription();
}
public double getCost() {
return decoratedCoffee.getCost();
}
}
// 3 创建具体装饰者类
// 具体装饰者类:牛奶
class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
public String getDescription() {
return super.getDescription() + ", Milk";
}
public double getCost() {
return super.getCost() + 0.5;
}
}
// 具体装饰者类:糖
class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
public String getDescription() {
return super.getDescription() + ", Sugar";
}
public double getCost() {
return super.getCost() + 0.2;
}
}
// 具体装饰者类:香草
class VanillaDecorator extends CoffeeDecorator {
public VanillaDecorator(Coffee coffee) {
super(coffee);
}
public String getDescription() {
return super.getDescription() + ", Vanilla";
}
public double getCost() {
return super.getCost() + 0.7;
}
}
// 4 客户端代码
public class CoffeeShop {
public static void main(String[] args) {
// 制作一杯简单咖啡
Coffee coffee = new SimpleCoffee();
System.out.println("Cost: $" + coffee.getCost() + "; Description: " + coffee.getDescription());
// 加牛奶
coffee = new MilkDecorator(coffee);
System.out.println("Cost: $" + coffee.getCost() + "; Description: " + coffee.getDescription());
// 再加糖
coffee = new SugarDecorator(coffee);
System.out.println("Cost: $" + coffee.getCost() + "; Description: " + coffee.getDescription());
// 再加香草
coffee = new VanillaDecorator(coffee);
System.out.println("Cost: $" + coffee.getCost() + "; Description: " + coffee.getDescription());
}
}
对比装饰器与静态代理模式
两者你单看代码案例的话,会发现,非常的相似,概括一下,都是有一个接口,然后目标类是实现了这个接口,然后代理类或者增强类也实现了这个接口,然后代理类或者增强类都持有目标对象接口的引用,都重写了接口方法,然后重写的方法中调用了目标方法,都可以添加自己的内容,这不能说相似,只能说一摸一样。既然代码层面上一模一样,那么他们就能达成相同的目标,我们常说静态代理可以做权限控制,功能增强。那么装饰器也一样可以作;我们说装饰器可以堆叠,做装饰器链,那么静态代理也可以做。但是我们不这么做,因为每种设计模式都有其侧重点,都是为了解决某种问题产生的,也就是说不能违背设计模式建立的初衷。你这么想,就能把装饰器和静态代理区分开了。
静态代理目标:增强对象、权限控制
装饰器目标:增强对象
- 门面模式
概述:为复杂系统,提供也给简单接口,对若干操作流程进行封装,尽可能少的暴露接口,符合迪米特原则
点击查看代码
// 子系统1
class Subsystem1 {
public void operation1() {
System.out.println("Subsystem1: Operation 1");
}
}
// 子系统2
class Subsystem2 {
public void operation2() {
System.out.println("Subsystem2: Operation 2");
}
}
// 子系统3
class Subsystem3 {
public void operation3() {
System.out.println("Subsystem3: Operation 3");
}
}
// 门面类
class Facade {
private Subsystem1 subsystem1;
private Subsystem2 subsystem2;
private Subsystem3 subsystem3;
public Facade() {
this.subsystem1 = new Subsystem1();
this.subsystem2 = new Subsystem2();
this.subsystem3 = new Subsystem3();
}
public void operationA() {
System.out.println("Facade: Operation A");
subsystem1.operation1();
subsystem2.operation2();
}
public void operationB() {
System.out.println("Facade: Operation B");
subsystem2.operation2();
subsystem3.operation3();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Facade facade = new Facade();
facade.operationA();
facade.operationB();
}
}
行为型设计模式
- 模板方法模式
概述:对完成的某个事件的过程进行分步、约束
点击查看代码
// 抽象工作模板类
abstract class WorkTemplate {
// 工作日常流程
public final void workDayRoutine() {
morningWork(); // 上午工作
lunchBreak(); // 午餐
afternoonWork(); // 下午工作
finishWork(); // 下班
}
// 抽象方法,上午工作
abstract void morningWork();
// 公共方法,午餐
void lunchBreak() {
System.out.println("Having lunch");
}
// 抽象方法,下午工作
abstract void afternoonWork();
// 抽象方法,下班
abstract void finishWork();
}
// 具体工作模板类 - 认真工作的员工
class DiligentWorker extends WorkTemplate {
@Override
void morningWork() {
System.out.println("Diligent worker is working diligently in the morning");
}
@Override
void afternoonWork() {
System.out.println("Diligent worker is working diligently in the afternoon");
}
@Override
void finishWork() {
System.out.println("Diligent worker finishes work and leaves on time");
}
}
// 具体工作模板类 - 划水摸鱼的员工
class Slacker extends WorkTemplate {
@Override
void morningWork() {
System.out.println("Slacker is slacking off in the morning");
}
@Override
void afternoonWork() {
System.out.println("Slacker is slacking off in the afternoon");
}
@Override
void finishWork() {
System.out.println("Slacker finishes work and leaves on time");
}
}
// 具体工作模板类 - 加班的员工
class OvertimeWorker extends WorkTemplate {
@Override
void morningWork() {
System.out.println("Overtime worker is working diligently in the morning");
}
@Override
void afternoonWork() {
System.out.println("Overtime worker is working diligently in the afternoon");
}
@Override
void finishWork() {
System.out.println("Overtime worker finishes work and stays overtime");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
WorkTemplate person1 = new DiligentWorker();
WorkTemplate person2 = new Slacker();
WorkTemplate person3 = new OvertimeWorker();
System.out.println("Diligent worker's work routine:");
person1.workDayRoutine();
System.out.println();
System.out.println("Slacker's work routine:");
person2.workDayRoutine();
System.out.println();
System.out.println("Overtime worker's work routine:");
person3.workDayRoutine();
}
}
- 策略模式
概述:为了解决某个问题,我们有一系列的方法,也就是策略,这些策略很多,就构成了算法族,算法族中的任何一个方法都可能被使用,这就要求不能强关联某个策略,所以算法族抽象出公共接口,我们依赖策略接口即可,很简单,很容易理解,这完全就是多态的使用,也是对依赖倒置原则的践行
点击查看代码
// 策略接口
interface PaymentStrategy {
void pay(int amount);
}
// 默认策略 - 使用现金支付
class DefaultPayment implements PaymentStrategy {
@Override
public void pay(int amount) {
System.out.println("Paying " + amount + " dollars in cash (default strategy)");
}
}
// 具体策略 - 使用信用卡支付
class CreditCardPayment implements PaymentStrategy {
private String cardNumber;
public CreditCardPayment(String cardNumber) {
this.cardNumber = cardNumber;
}
@Override
public void pay(int amount) {
System.out.println("Paying " + amount + " dollars using Credit Card ending with " + cardNumber.substring(cardNumber.length() - 4));
}
}
// 上下文 - 订单类
class Order {
private PaymentStrategy paymentStrategy;
// 默认使用现金支付
public Order() {
this.paymentStrategy = new DefaultPayment();
}
public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void processPayment(int amount) {
paymentStrategy.pay(amount);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Order order = new Order();
// 客户选择信用卡支付
order.setPaymentStrategy(new CreditCardPayment("1234567812345678"));
order.processPayment(100);
// 客户未指定支付方式,默认使用现金支付
Order order2 = new Order();
order2.processPayment(50);
}
}
- 命令模式
概述:命令模式涉及到三个部分,1发送者,2命令,3接收者,假设不用命令模式,我们的发送者直接调用接收者,这个耦合度很高,首先,发送者调用接收者做不同的事情,其次接收者又是多种多样的,这就要求发送者有众多的方法,来调用不同的接收者,然后还要完成不同的事情。如果有了新的接收者,或者需要调用接收者某个新功能,就需要修改发送者的代码。开闭原则了解一下
下面分析使用命令模式的情况,我们引入命令的概念,所谓命令,很好理解,举个生活中的例子,按下空调遥控开机键,空调就打开了,这就是一个命令,这个命令包含了开机按钮按下发射的信号,同时也包含了设备,这是一个整体。有了这个命令,空调就会做出对应的动作,相当于,我作为发送者,通过一个命令,调用了接收者,也就是空调。我不用关系空调怎么开机,我只需要发一个命令,再强调一下这个命令,这个命令是包含了执行操作和接收者的,是不是解耦合了。
总结一下上面的例子,这体现了调用者参数化
调用接收者,不必关注调用的细节
接收者接收到命令一定要立刻执行吗,那不一定,命令的处理逻辑是在接收者里边写的,接收者想什么时候执行,就什么时候执行,这就是命令模式的另一个优势,延时执行请求,举个例子,比如空调定时关机。
抽离变化的,保留不变的
对于命令模式,谁是变化的,接收者是变化的,我一会开空调,一会开电视。其次就是针对某个特定的接收者,我要执行的命令是不同的,比如空调升温,降温,开机关机
所以这两个内容就被抽取出来了,抽取成了命令接口,也就是案例中的TurnOnDeviceCommand、TurnOffDeviceCommand两个类。
点击查看代码
// 1 设备接口
interface Device {
void turnOn();
void turnOff();
}
// 2 具体设备 - 电视
class Television implements Device {
@Override
public void turnOn() {
System.out.println("Turning on the Television");
}
@Override
public void turnOff() {
System.out.println("Turning off the Television");
}
}
// 具体设备 - 空调
class AirConditioner implements Device {
@Override
public void turnOn() {
System.out.println("Turning on the Air Conditioner");
}
@Override
public void turnOff() {
System.out.println("Turning off the Air Conditioner");
}
}
// 3 命令接口
interface Command {
void execute();
}
// 4 具体命令 - 开启设备
class TurnOnDeviceCommand implements Command {
private Device device;
public TurnOnDeviceCommand(Device device) {
this.device = device;
}
@Override
public void execute() {
device.turnOn();
}
}
// 具体命令 - 关闭设备
class TurnOffDeviceCommand implements Command {
private Device device;
public TurnOffDeviceCommand(Device device) {
this.device = device;
}
@Override
public void execute() {
device.turnOff();
}
}
// 5 调用者 - 遥控器
class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}
// 6 客户端代码
public class Client {
public static void main(String[] args) {
// 创建具体设备
Device tv = new Television();
Device ac = new AirConditioner();
// 创建具体命令
Command turnOnTVCommand = new TurnOnDeviceCommand(tv);
Command turnOffACCommand = new TurnOffDeviceCommand(ac);
// 创建调用者
RemoteControl remoteControl = new RemoteControl();
// 执行命令
remoteControl.setCommand(turnOnTVCommand);
remoteControl.pressButton(); // 打开电视
remoteControl.setCommand(turnOffACCommand);
remoteControl.pressButton(); // 关闭空调
}
}
- 责任链模式
作用:1.将请求发起者与多个潜在请求处理者解耦,假设没有责任链,发起一个问题后,要知道所有的处理者能处理什么内容,但是我只是提出一个问题,至于怎么解决,我不关心,不使用责任链的时候,我就需要对问题进行判断,发生A问题,我需要找A处理者,B问题,就需要找B处理者。引入责任链后,我只需要将问题抛给责任链,责任链依次处理。我就无需关注问题的处理者能解决什么问题。2.职责划分明确,每个处理者只关注自己能处理的内容。
点击查看代码
// 抽象处理者
abstract class FamilyMember {
protected FamilyMember nextMember;
public void setNextMember(FamilyMember nextMember) {
this.nextMember = nextMember;
}
public abstract void handleRequest(String problem);
}
// 具体处理者:孩子
class Child extends FamilyMember {
public void handleRequest(String problem) {
if (problem.equals("玩具坏了") || problem.equals("遥控器没电了")) {
System.out.println("孩子解决问题: " + problem);
} else if (nextMember != null) {
nextMember.handleRequest(problem);
}
}
}
// 具体处理者:父母
class Parent extends FamilyMember {
public void handleRequest(String problem) {
if (problem.equals("灯泡需要更换") || problem.equals("水龙头漏水")) {
System.out.println("父母解决问题: " + problem);
} else if (nextMember != null) {
nextMember.handleRequest(problem);
}
}
}
// 具体处理者:专业维修人员
class ProfessionalRepairman extends FamilyMember {
public void handleRequest(String problem) {
System.out.println("专业维修人员解决问题: " + problem);
}
}
// 测试责任链模式
public class ChainOfResponsibilityDemo {
public static void main(String[] args) {
// 创建处理链
FamilyMember child = new Child();
FamilyMember parent = new Parent();
FamilyMember repairman = new ProfessionalRepairman();
child.setNextMember(parent);
parent.setNextMember(repairman);
// 测试处理不同的问题
child.handleRequest("玩具坏了");
child.handleRequest("水龙头漏水");
child.handleRequest("管道爆裂");
}
}
- 状态模式
概述:状态模式用于将实体的状态和行为分离,实现了状态和行为的解耦,这么说很抽象,我先举例,后概括
案例:假设我要设计一款生存类游戏,我为角色设置了健康系统,健康系统包括以下几个状态,正常、饥饿、受伤、死亡,当我们的角色处于不同的状态的时候,身体的行为也会发生变化。这里我就引出状态模式三个概念,状态、行为、角色。三者的关系是,角色在不同的状态下会有不同的行为。现在我们尝试实现这个内容,假设没有使用状态模式,我需要在角色类中定义一个活动的方法,然后这个方法中需要根据当前角色的状态,做出调整,比如饥饿状态,角色移动变慢;受伤状态,角色攻击力下降等等。这样我们如果后期加入了其他的状态,比如角色中毒,我们就需要修改角色类中的行为,加一个判断,如果中毒了,角色就慢慢掉血。至此,问题已经暴露了,就是角色和状态行为之间,耦合性太强了。每多一种状态,都得修改角色的代码。
下面我们分析使用状态模式的情况,首先将状态抽象,内部定义不同的行为接口,意思就是说,某种状态下,对角色的行为进行了抽象。然后需要有不同的真实状态去实现状态接口,并对这些不同的行为做出当前状态下的实现,比如嗑药之后,攻击力增强,移速加快。然后角色持有状态的示例,角色在活动的时候,就会根据当前状态中的行为,做出不同的动作,如果要扩展新的状态,那么也不用再修改角色类了。
总结:状态模式,就是将对象的状态和行为从对象中抽离,避免使用大量的if-else判断,这使对象和状态行为解耦,易于添加新的状态。
状态-行为组合这种问题,叫做多维度变化问题,会产生笛卡尔积,桥接模式也是解决排列组合问题
点击查看代码
// 状态接口
interface HealthState {
void handleMovement(); // 处理移动行为
void handleAttack(); // 处理攻击行为
}
// 具体状态类:正常状态
class NormalState implements HealthState {
public void handleMovement() {
System.out.println("正常状态下移动速度正常");
}
public void handleAttack() {
System.out.println("正常状态下攻击力正常");
}
}
// 具体状态类:饥饿状态
class HungryState implements HealthState {
public void handleMovement() {
System.out.println("饥饿状态下移动速度减慢");
}
public void handleAttack() {
System.out.println("饥饿状态下攻击力减弱");
}
}
// 具体状态类:受伤状态
class InjuredState implements HealthState {
public void handleMovement() {
System.out.println("受伤状态下移动速度减慢");
}
public void handleAttack() {
System.out.println("受伤状态下攻击力减弱");
}
}
// 具体状态类:死亡状态
class DeadState implements HealthState {
public void handleMovement() {
System.out.println("死亡状态下不能移动");
}
public void handleAttack() {
System.out.println("死亡状态下不能攻击");
}
}
// 上下文类:角色类
class Player {
private HealthState currentState;
public Player() {
currentState = new NormalState(); // 初始状态为正常状态
}
public void setState(HealthState state) {
currentState = state;
}
public void move() {
currentState.handleMovement();
}
public void attack() {
currentState.handleAttack();
}
}
// 测试状态模式
public class StatePatternDemo {
public static void main(String[] args) {
Player player = new Player();
player.move(); // 正常状态下移动速度正常
player.attack(); // 正常状态下攻击力正常
player.setState(new HungryState());
player.move(); // 饥饿状态下移动速度减慢
player.attack(); // 饥饿状态下攻击力减弱
player.setState(new InjuredState());
player.move(); // 受伤状态下移动速度减慢
player.attack(); // 受伤状态下攻击力减弱
player.setState(new DeadState());
player.move(); // 死亡状态下不能移动
player.attack(); // 死亡状态下不能攻击
}
}
- 观察者模式
概述:如果观察者要根据某个目标的行为做出对应的动作,观察者需要一直监视目标的状态,比如轮询监视。这使得观察者和目标两个角色的关系耦合度很高因为观察者需要浪费大量的精力去盯着目标,举个例子,现在有某个社交平台,小明非常喜欢某个up发布的学习资料,于是小明经常去该up的主页查看,up是否更新了内容,这使得小明非常困扰,因为up发布视频的时间不固定,为了解决这个问题,社交平台推出了订阅关注的功能,小明订阅了这个喜欢的up,当up更新内容时,小明会自动收到推送,然后观看就可以了。
点击查看代码
// 1 观察者接口
interface Observer {
void update(String message);
}
// 具体观察者
class User implements Observer {
private String name;
public User(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " received notification: " + message);
}
}
// 2 目标接口
interface Subject {
void subscribe(Observer o);
void unsubscribe(Observer o);
void notifyObservers(String message);
}
import java.util.ArrayList;
import java.util.List;
// 具体目标
class UpUser implements Subject {
private List<Observer> observers = new ArrayList<>();
private String name;
public UpUser(String name) {
this.name = name;
}
@Override
public void subscribe(Observer o) {
observers.add(o);
}
@Override
public void unsubscribe(Observer o) {
observers.remove(o);
}
@Override
public void notifyObservers(String message) {
for (Observer observer : observers) {
observer.update(name + " posted: " + message);
}
}
public void postNewContent(String content) {
System.out.println(name + " posted: " + content);
notifyObservers(content);
}
}
//3 测试
public class ObserverPatternDemo {
public static void main(String[] args) {
// 创建目标对象
UpUser upUser = new UpUser("Favorite UP");
// 创建观察者对象
User xiaoming = new User("Xiaoming");
// 订阅
upUser.subscribe(xiaoming);
// UP主发布新内容
upUser.postNewContent("New learning materials are available!");
// 再次发布新内容
upUser.postNewContent("Advanced Java tutorials released!");
// 取消订阅
upUser.unsubscribe(xiaoming);
// 再次发布新内容(此时小明已经取消订阅,不会收到通知)
upUser.postNewContent("Spring Framework guide updated!");
}
}
- 中介者设计模式
概述:中介者模式是主要的作用是,减少用户面对的对象,减少用户为了解决一个问题所需要面向的其他服务提供者。听起来跟门面模式很像。在代码上看,确实跟门面很像。但是中介者,中介解决的问题主要还是资源整合,不强制用户禁止访问中介中的服务提供者,而门面给人的感觉就是,你只能访问我这个门面,屏蔽的感觉更强烈
点击查看代码
// 1 中介者接口定义了系统中的操作
interface InventoryMediator {
// 进货操作
void purchaseItem(String item, int quantity);
// 销售操作
void sellItem(String item, int quantity);
// 查看库存操作
void checkInventory(String item);
}
// 2 具体中介者实现了中介者接口,协调了各个子系统的操作
class InventoryMediatorImpl implements InventoryMediator {
private PurchaseSystem purchaseSystem; // 进货系统
private SalesSystem salesSystem; // 销售系统
private InventorySystem inventorySystem; // 库存管理系统
// 初始化中介者时,创建各个子系统对象
public InventoryMediatorImpl() {
this.purchaseSystem = new PurchaseSystem();
this.salesSystem = new SalesSystem();
this.inventorySystem = new InventorySystem();
}
// 中介者实现进货操作
@Override
public void purchaseItem(String item, int quantity) {
purchaseSystem.purchase(item, quantity); // 调用进货系统进行进货操作
inventorySystem.addInventory(item, quantity); // 更新库存系统的库存
}
// 中介者实现销售操作
@Override
public void sellItem(String item, int quantity) {
// 检查是否有足够的库存
if (inventorySystem.getInventory(item) >= quantity) {
salesSystem.sell(item, quantity); // 调用销售系统进行销售操作
inventorySystem.removeInventory(item, quantity); // 更新库存系统的库存
} else {
System.out.println("Not enough inventory for item: " + item);
}
}
// 中介者实现查看库存操作
@Override
public void checkInventory(String item) {
inventorySystem.checkInventory(item); // 调用库存系统查看库存
}
}
// 3 进货系统,实现了进货操作
class PurchaseSystem {
public void purchase(String item, int quantity) {
System.out.println("Purchased " + quantity + " of " + item);
}
}
// 销售系统,实现了销售操作
class SalesSystem {
public void sell(String item, int quantity) {
System.out.println("Sold " + quantity + " of " + item);
}
}
// 库存管理系统,实现了库存管理操作
class InventorySystem {
private Map<String, Integer> inventory = new HashMap<>(); // 库存记录,使用Map存储
// 添加库存
public void addInventory(String item, int quantity) {
inventory.put(item, inventory.getOrDefault(item, 0) + quantity);
System.out.println("Added " + quantity + " of " + item + " to inventory");
}
// 减少库存
public void removeInventory(String item, int quantity) {
inventory.put(item, inventory.get(item) - quantity);
System.out.println("Removed " + quantity + " of " + item + " from inventory");
}
// 获取指定商品的库存量
public int getInventory(String item) {
return inventory.getOrDefault(item, 0);
}
// 查看库存
public void checkInventory(String item) {
System.out.println("Inventory for " + item + ": " + getInventory(item));
}
}
// 4 客户类,通过中介者进行购买操作
class Customer {
private InventoryMediator mediator;
// 初始化客户时传入中介者对象
public Customer(InventoryMediator mediator) {
this.mediator = mediator;
}
// 客户购买商品的方法,调用中介者的销售操作
public void buyItem(String item, int quantity) {
mediator.sellItem(item, quantity);
}
}
// 5
public class MediatorPatternInventoryDemo {
public static void main(String[] args) {
InventoryMediator mediator = new InventoryMediatorImpl(); // 创建中介者对象
Customer customer = new Customer(mediator); // 创建客户对象,传入中介者
// 模拟系统运行
// 添加库存
mediator.purchaseItem("Laptop", 10);
mediator.purchaseItem("Phone", 20);
// 检查库存
mediator.checkInventory("Laptop");
mediator.checkInventory("Phone");
// 客户购买商品
customer.buyItem("Laptop", 5);
customer.buyItem("Phone", 15);
// 再次检查库存
mediator.checkInventory("Laptop");
mediator.checkInventory("Phone");
// 尝试购买超出库存的数量
customer.buyItem("Laptop", 10);
}
}