设计模式的目的
- 代码从重用性(相同的功能可以复用)
- 代码的可读性(编程的规范性,便于其他人的阅读)
- 可扩展性(程序添加新功能不会很困难)
- 可靠性(添加新功能后对原有的功能没有影响)
- 使程序高内聚低耦合
七大设计原则
1.单一职责原则
介绍
- 一个类应该只负责一个职责,降低类的耦合度。
- 提高了类的可读性,可维护性。
- 降低修改影响其他功能的风险。
- 通常情况下需要遵守单一职责原则,如果逻辑足够简单,就可以在代码级别违反单一职责。类的方法足够少则可以在方法级别违反。
问题
/**
* @program: DesignPattern
* @author: Mr.bai
* @create: 2022-07-05 19:43
**/
package com.bai.responsibility;
public class SingleResponsibility1 {
public static void main(String[] args) {
Vehicle vehicle = new Vehicle();
vehicle.run("摩托车");
vehicle.run("飞机");
vehicle.run("轮船");
}
}
/**
* 问题:违反了单一职责原则,一个类中有多个职责 导致功能杂糅,修改的代价会变大可能影响到别的功能
* 解决:把功能依据分类进行拆分,让一个类的功能尽可能原子化,逻辑简单。好维护且可读性高。
*/
class Vehicle {
public void run(String vehicle) {
System.out.println(vehicle + "在公路上跑");
}
}
解决
/**
* @program: DesignPattern
* @author: Mr.bai
* @create: 2022-07-05 19:43
**/
package com.bai.responsibility;
public class SingleResponsibility2 {
public static void main(String[] args) {
new RoadVehicle().run("摩托车");
new AirVehicle().run("飞机");
new WaterVehicle().run("潜水艇");
}
}
/**
* 陆地交通工具
*/
class RoadVehicle {
public void run(String airVehicle) {
System.out.println(airVehicle + "在公路上跑");
}
}
/**
* 空中交通工具
*/
class AirVehicle {
public void run(String airVehicle) {
System.out.println(airVehicle + "在天上飞");
}
}
/**
* 水中交通工具
*/
class WaterVehicle {
public void run(String waterVehicle) {
System.out.println(waterVehicle + "在水里游");
}
}
2.接口隔离职责
- 一个类对另一个类的依赖应该建立在一个比较小的接口上
问题
A类只需要123三个方法,但实现接口时必须得实现不需要的方法,造成了无用的开销
interface interface1 {
void operation1();
void operation2();
void operation3();
void operation4();
void operation5();
}
解决
A类需要123这三个方法,可直接实现 interface A interface B这两个接口 实现的方法都是我需要的
interface interfaceA {
void operation1();
}
interface interfaceB {
void operation2();
void operation3();
}
interface interfaceC {
void operation4();
void operation5();
}
3.依赖倒置原则
基本介绍
- 抽象不应该依赖具体,具体应该依赖抽象
- 面向接口编程
- 使用接口和抽象类制定规范,具体的细节给子类完成
- 使用接口和抽象类的目的主要是制定好规范,里面不应该有具体的操作,具体的细节由子类完成。
注意事项:
- 底层模块 类 都尽量有抽象类,接口。或者两者都有。接口制定规范让程序更加稳定。
- 变量的声明类型应该尽量为抽象类型,在变量的引用期间就会有缓存层,利于程序的扩展和优化。
- 遵循里氏替换原则
问题
/**
* @program: DesignPattern
* @author: Mr.bai
* @create: 2022-07-22 16:53
* <p>
* 依赖倒置原则问题描述
**/
package com.bai.dependInversion;
public class DependInversionProblem {
public static void main(String[] args) {
Person person = new Person();
person.receive(new Email());
}
}
class Email {
public String sendInfo() {
return "发送了电子邮件";
}
}
class Person {
/**
* 当前的方法直接依赖了一个具体的类email 导致程序的扩展
* 性很差,当我们需要接受微信 短信等其他信息时 就出问题了
* 可以增加一个Receiver接口 所有发信息的类都实现这个接口。
*/
public void receive(Email email) {
System.out.println(email.sendInfo());
}
}
4.里氏替换原则
介绍
- 所有引用父类的地方都必须能够透明的使用其子类的对象(功能不发生改变)。
- 使用继承时,遵循里氏替换原则 尽量不要重写父类的方法。
- 继承让两个类的耦合度增加了,如果想使用其他类的方法,可以使用聚合、组合、依赖来调用。
5.开闭原则
介绍
- 对扩展开放,对修改关闭。
- 当软件发生改变时,尽量通过扩展来实现变化,而不是修改原有代码实现变化。
问题
/**
* 开放关闭原则
**/
package com.bai.ocp;
public class Ocp01 {
public static void main(String[] args) {
new Draw(new Circle());
new Draw(new Rectangle());
}
}
/**
* 不符合开放封闭原则,每次增加新的功能都会对原来的代码修改。
*/
class Draw {
public Draw(Shape shape) {
if (shape.type == 0) {
System.out.println("画圆形");
System.out.println("画圆形");
} else if (shape.type == 1) {
System.out.println("画矩形");
}
}
}
class Shape {
int type;
}
class Circle extends Shape {
public Circle() {
super.type = 0;
}
}
//矩形
class Rectangle extends Shape {
public Rectangle() {
super.type = 1;
}
}
解决
/**
* 开放关闭原则
**/
package com.bai.ocp;
public class Ocp02 {
public static void main(String[] args) {
new DrawPlus(new CirclePlus());
new DrawPlus(new RectanglePlus());
}
}
//使用方
class DrawPlus {
public DrawPlus(ShapePlus shape) {
shape.draw();
}
}
abstract class ShapePlus {
abstract void draw();
}
class CirclePlus extends ShapePlus {
@Override
void draw() {
System.out.println("绘制圆形");
}
}
//矩形
class RectanglePlus extends ShapePlus {
@Override
void draw() {
System.out.println("绘制矩形");
}
}
6.迪米特法则
介绍
- 一个对象对其他对象保持最少的了解。
- 类与类直接的关系越密切,耦合度越大。
- 最少知道原则:对自己依赖的类知道的越少越好(少管我原则),你自己类中的逻辑就放在自己的类中,对外提供public的方法,不对外泄露信息。
- 只于直接朋友通(直接朋友:类的成员变量、返回值类型、方法的形参,其他都属于陌生朋友 迪米特不和陌生朋友打交道)
注意事项
迪米特法则的观点是尽可能的降低类直接的耦合度而不是完全没有依赖
/**
* @program: DesignPattern
* @author: Mr.bai
* @create: 2022-07-23 20:42
**/
package com.bai.Responsbility.demeter;
import java.util.ArrayList;
import java.util.List;
public class Demeter {
public static void main(String[] args) {
new SchoolEmployeeManger().printAllEmployee(new CollageManger());
}
}
//学校员工类
class SchoolEmployee {
private int id;
public SchoolEmployee(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
//学院员工类
class CollageEmployee {
private int id;
public CollageEmployee(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
//学院员工管理类
class CollageManger {
public List<CollageEmployee> getAllCollageEmployee() {
List<CollageEmployee> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(new CollageEmployee(i));
}
return list;
}
}
//学校员工管理类
/**
* 直接朋友定义:类的成员变量、返回值类型、方法的形参
* SchoolEmployeeManger直接朋友:SchoolEmployee CollageManger
* SchoolEmployeeManger陌生朋友: CollageEmployee(局部变量) 违反了demeter法则
*/
class SchoolEmployeeManger {
public List<SchoolEmployee> getAllSchoolEmployee() {
List<SchoolEmployee> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
list.add(new SchoolEmployee(i));
}
return list;
}
//打印所有员工
public void printAllEmployee(CollageManger collageManger) {
//获取所有的学院员工
System.out.println("学院员工:");
//CollageEmployee是陌生朋友 在当前方法中写了CollageEmployee内部的逻辑 应该丢到他自己的方法中
List<CollageEmployee> allCollageEmployee = collageManger.getAllCollageEmployee();
for (CollageEmployee collageEmployee : allCollageEmployee) {
System.out.println(collageEmployee.getId());
}
//获取所有的学校总部员工
System.out.println("总部员工");
List<SchoolEmployee> allSchoolEmployee = this.getAllSchoolEmployee();
for (SchoolEmployee schoolEmployee : allSchoolEmployee) {
System.out.println(schoolEmployee.getId());
}
}
}
改进
自己的业务逻辑写在自己的类中,对外提供public方法不要暴漏内部细节
/**
* @program: DesignPattern
* @author: Mr.bai
* @create: 2022-07-23 20:42
**/
package com.bai.Responsbility.demeter.inprovent;
import java.util.ArrayList;
import java.util.List;
public class Demeter {
public static void main(String[] args) {
new SchoolEmployeeManger().printAllEmployee(new CollageManger());
}
}
//学校员工类
class SchoolEmployee {
private int id;
public SchoolEmployee(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
//学院员工类
class CollageEmployee {
private int id;
public CollageEmployee(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
//学院员工管理类
class CollageManger {
public List<CollageEmployee> getAllCollageEmployee() {
List<CollageEmployee> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(new CollageEmployee(i));
}
return list;
}
public void printAllCollageEmployee(){
//获取所有的学院员工
System.out.println("学院员工:");
//CollageEmployee是陌生朋友 在当前方法中写了CollageEmployee内部的逻辑 应该丢到他自己的方法中
List<CollageEmployee> allCollageEmployee = this.getAllCollageEmployee();
for (CollageEmployee collageEmployee : allCollageEmployee) {
System.out.println(collageEmployee.getId());
}
}
}
//学校员工管理类
/**
* 直接朋友定义:类的成员变量、返回值类型、方法的形参
* SchoolEmployeeManger直接朋友:SchoolEmployee CollageManger
* SchoolEmployeeManger陌生朋友: CollageEmployee(局部变量) 违反了demeter法则
*/
class SchoolEmployeeManger {
public List<SchoolEmployee> getAllSchoolEmployee() {
List<SchoolEmployee> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
list.add(new SchoolEmployee(i));
}
return list;
}
//打印所有员工
public void printAllEmployee(CollageManger collageManger) {
collageManger.printAllCollageEmployee();
//获取所有的学校总部员工
System.out.println("总部员工");
List<SchoolEmployee> allSchoolEmployee = this.getAllSchoolEmployee();
for (SchoolEmployee schoolEmployee : allSchoolEmployee) {
System.out.println(schoolEmployee.getId());
}
}
}
7.合成复用原则
原则
尽量使用合成/聚合的方式,而不是使用继承
七大原则总结
ULM类图
1.依赖
定义:在类中用到了其他对象都是依赖{
-
- 成员属性
- 方法返回值
- 方法参数
- 方法内
}
2.泛化
泛化是依赖的特殊形式 子类继承父类 父类泛化子类
3.实现
实现也是依赖的特殊形式 A类实现B接口
4.关联
是一种特殊的依赖关系 一个类中有对象的成员变量
5.聚合
是一种特殊的关联关系 一个类中有另一个类的成员变量,但是这个成员变量是可以移除或者更换的
6.组合
是一种特殊的关联关系 一个类中有另一个类的成员变量,但是这个成员变量是这个类的一部分 不可以修改或删除
设计模式分类
桥代理组合适配器 享元回家修饰外观
- 创建型模式用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。GoF(四人组)书中提供了单例、原型、工厂方法、抽象工厂、建造者等 5 种创建型模式。
- 结构型模式用于描述如何将类或对象按某种布局组成更大的结构,GoF(四人组)书中提供了代理、适配器、桥接、装饰、外观、享元、组合等 7 种结构型模式。
- 行为型模式用于描述类或对象之间怎样相互协作共同完成单个对象无法单独完成的任务,以及怎样分配职责。GoF(四人组)书中提供了模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器等 11 种行为型模式。
创建型 | 结构型 | 行为型 | |
---|---|---|---|
类 | 工厂方法模式 | 适配器模式(类) | 解释器模式 |
模板方法模式 | |||
对象 | 抽象工厂模式 | 适配器模式(对象) | 责任链模式 |
生成器模式 | 桥接模式 | 命令模式 | |
原型模式 | 代理模式 | 迭代器模式 | |
单例模式 | 组合模式 | 中介者模式 | |
享元模式 | 备忘录模式 | ||
装饰器模式 | 观察者模式 | ||
外观模式 | 状态模式 | ||
策略模式 | |||
访问者模式 |
创建型
1.单例模式(singlepatten)
饿汉式1(建议):
优点:代码简单,且在类加载时就创建实例,避免了线程同步的问题
缺点:造成实例的浪费
class Singleton {
//1.创建私有静态常量
private final static Singleton instance = new Singleton();
//2.私有化构造器
private Singleton() {
}
//3.对外提供方法
public static Singleton getInstance() {
return instance;
}
}
饿汉式2(静态代码块 建议)
/**
* 饿汉单例模式实现2:
* 静态代码块中创建实例
*/
class Singleton2 {
//1.创建私有静态常量
private final static Singleton2 instance;
//2.静态代码块中创建实例
static {
instance = new Singleton2();
}
//3.私有化构造器
private Singleton2() {
}
//4.对外提供方法
public static Singleton2 getInstance() {
return instance;
}
}
懒汉式(不建议):
优点:可以懒加载。
缺点:线程不安全。
实际项目中不建议使用
class Singleton {
//1.创建私有静态常量不赋值
private static Singleton instance = null;
//2.私有化构造器
private Singleton() {
}
//3.对外提供方法,使用时才创建对象
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
懒汉式2(线程安全 效率低)
优点:线程安全
缺点:效率太低
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
懒汉式3(双重检查 建议):
解决指令重排,多线程并发 并且效率提升
对于两次检查的理解:
内部的if是用来保证代码块同步。
外部的代码块为了提高程序效率,不必所有线程进入方法都进入同步代码块排队。
class Singleton {
//1.创建私有静态常量不赋值
private static volatile Singleton instance = null;
//2.私有化构造器
private Singleton() {
}
//3.对外提供方法,使用时才创建对象
//解决指令重排并且效率提升
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
}
静态内部类(完美):
原理:
*1. 外部类加载的时候 静态内部类不被加载
**2. 调用getInstance()方法的时候才会被加载 且只加载一次 并且实现了懒加载
*3. jvm保证线程安全
class Singleton {
private Singleton() {
}
// 静态的属性在类加载时初始化保证了单例 和 线程同步的问题
private static class SingletonInstance {
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.instance;
}
}
枚举(最完美)
enum Singleton{
instance,
}
简单工厂模式(simply factory)
介绍
在工厂中判断要什么商品就创建什么商品
缺点
每次添加商品都需要在工厂中进行修改
2.工厂方法模式(factory method)创建型对象模式
介绍
每添加一个产品就添加一个工厂的实现类
优点
- 添加新咖啡只需要在设计一个咖啡对象 和 一个咖啡工厂,无需修改其他的类 符合开放封闭原则。
- 客户端使用时只需知道工厂的名字,就可以获得对象,无需知道工厂内部的实现细节
缺点
每添加一个咖啡就需要一个咖啡工厂,增加的系统的复杂度
3.抽象工厂模式(abstract factory)
介绍
有一个顶级抽象工厂,抽象工厂的作用是用来生产工厂的,每个工厂可以生产符合自己产品族的产品。例如 华为的工厂可以生产华为的一系列不同级别的产品。例如电脑 平板 手机 产品级别不同但是属于一个产品族。
优点:
一个工厂可以创建一系列的商品。
缺点
添加产品族困难 需要修改抽象工厂。
4.原型模式
介绍
以某个对象为原型,克隆出另一个对象。
场景
某个类对象创建特别复杂,需要很多行代码。使用原型模式 就可以对原型进行copy,加以修改就可以使用。
浅拷贝
在Java中使用原型模式只需要实现Cloneabel接口并且重写clone方法即可 ,默认浅克隆。
package com.bai.partten4.prototyep;
import java.util.Date;
public class Sheep implements Cloneable {
private String name;
private Date birthTime;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
问题
浅拷贝中对象的基本类型属性会复制一份给拷贝对象,但是对于引用类型的数据,会copy引用的内存地址。导致原型和copy对象指向同一个内存空间。原型修改,copy对象引用属性也会修改,可能造成业务的bug。为解决这个问题,我们可以重写clone对象,完成深拷贝。
深拷贝
/**
* @program: DesignPattern
* @author: Mr.bai
* @create: 2022-07-30 15:47
**/
package com.bai.partten4.prototyep;
import java.util.Date;
public class SheepDeepClone implements Cloneable{
private String name;
private Date birthTime;
public SheepDeepClone() {
}
@Override
protected Object clone() throws CloneNotSupportedException {
SheepDeepClone v = (SheepDeepClone) super.clone();
v.setBirthTime((Date) this.birthTime.clone());
return v;
}
}
5.生成器模式
介绍
builder负责实现对象的细节部分,指挥者负责按照顺序调用builder的细节部分并返回具体对象
虽然简化了系统结构,但是不符合单一职责原则,
优点
- 封闭性良好
- 产品和产品的构建解耦合,使得相同的创建过程可以构建不同的对象
- 创建过程清晰
- 扩展很方便,只需要添加构建者类即可
缺点
构建者模式构建的对象都具有一定的相似,因此若产品之间差异较大,则不适合使用构建者模式。
扩展
对于创建一个类需要多个构造参数,这样会导致代码的可读性很差。且容易发生错误,我们可以使用构造者模式对创建一个对象中部分的分类,让语义清晰,把构建的顺序的权力交给cpu。
package com.bai.partten5.builder.extend;
public class Phone {
private String cpu;
private String mainBoard;
private String memory;
private String screen;
private Phone(Builder builder) {
this.cpu = builder.cpu;
this.mainBoard = builder.mainBoard;
this.memory = builder.memory;
this.screen = builder.screen;
}
public static final class Builder {
private String cpu;
private String mainBoard;
private String memory;
private String screen;
public Builder cpu(String cpu) {
this.cpu = cpu;
return this;
}
public Builder mainBoard(String mainBoard) {
this.mainBoard = mainBoard;
return this;
}
public Builder memory(String memory) {
this.memory = memory;
return this;
}
public Builder screen(String screen) {
this.screen = screen;
return this;
}
public Phone construct() {
return new Phone(this);
}
}
@Override
public String toString() {
return "Phone{" +
"cpu='" + cpu + '\'' +
", mainBoard='" + mainBoard + '\'' +
", memory='" + memory + '\'' +
", screen='" + screen + '\'' +
'}';
}
}
public class Client {
public static void main(String[] args) {
Phone phone = new Phone.Builder()
.cpu("麒麟")
.memory("小米")
.mainBoard("华为")
.screen("中兴")
.construct();
System.out.println(phone);
}
}
总结
工厂方法模式
一个产品一个工厂,产品都一样。
抽象工厂
品牌 的一系列产品
建造者模式
代工厂组装产品
结构性模式
6.代理模式
静态代理
理解
售票中介,方便我们买票。
优点:
- 可以使真实角色的职责更加单一,符合单一职责原则。
- 公共的业务交给代理角色,实现业务的分工。
- 公共业务发生扩展的时候,可以不改变原有代码的情况下新增加一些功能。例如打印日志,自动抢票...。,方便集中管理。(开放关闭原则)。
缺点:
一个真实的角色就会产生一个带来对象,代码量翻倍
结构
动态代理
结构
//卖票接口
public interface SellTickets {
void sell();
}
//火车站 火车站具有卖票功能,所以需要实现SellTickets接口
public class TrainStation implements SellTickets {
public void sell() {
System.out.println("火车站卖票");
}
}
//代理工厂,用来创建代理对象
public class ProxyFactory {
private TrainStation station = new TrainStation();
public SellTickets getProxyObject() {
//使用Proxy获取代理对象
/*
newProxyInstance()方法参数说明:
ClassLoader loader : 类加载器,用于加载代理类,使用真实对象的类加载器即可
Class<?>[] interfaces : 真实对象所实现的接口,代理模式真实对象和代理对象实现相同的接口
InvocationHandler h : 代理对象的调用处理程序
*/
SellTickets sellTickets = (SellTickets) Proxy.newProxyInstance(station.getClass().getClassLoader(),
station.getClass().getInterfaces(),
new InvocationHandler() {
/*
InvocationHandler中invoke方法参数说明:
proxy : 代理对象
method : 对应于在代理对象上调用的接口方法的 Method 实例
args : 代理对象调用接口方法时传递的实际参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理点收取一些服务费用(JDK动态代理方式)");
//执行真实对象
Object result = method.invoke(station, args);
return result;
}
});
return sellTickets;
}
}
//测试类
public class Client {
public static void main(String[] args) {
//获取代理对象
ProxyFactory factory = new ProxyFactory();
SellTickets proxyObject = factory.getProxyObject();
proxyObject.sell();
}
}
动态代理和静态代理对比
- 动态代理类内部自动实现接口的方法,维护简单。
- 动态代理所有接口中的方法都进行的集中管理,对于大量的公共能,适合用动态代理。
//程序运行过程中动态生成的代理类
public final class $Proxy0 extends Proxy implements SellTickets {
private static Method m3;
private static Method m4;
public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}
static {
m3 = Class.forName("com.itheima.proxy.dynamic.jdk.SellTickets").getMethod("sell", new Class[0]);
m4 = Class.forName("com.itheima.proxy.dynamic.jdk.SellTickets").getMethod("sellTicket", new Class[0]);
}
public final void sell() {
this.h.invoke(this, m3, null);
}
}
//Java提供的动态代理相关类
public class Proxy implements java.io.Serializable {
protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
this.h = h;
}
}
//代理工厂类
public class ProxyFactory {
private TrainStation station = new TrainStation();
public SellTickets getProxyObject() {
SellTickets sellTickets = (SellTickets) Proxy.newProxyInstance(station.getClass().getClassLoader(),
station.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//优点 多个方法都走这个一个程序调用不同方法不同。
System.out.println("代理点收取一些服务费用(JDK动态代理方式)");
Object result = method.invoke(station, args);
return result;
}
});
return sellTickets;
}
}
//测试访问类
public class Client {
public static void main(String[] args) {
//获取代理对象
ProxyFactory factory = new ProxyFactory();
SellTickets proxyObject = factory.getProxyObject();
proxyObject.sell();
}
}
7.适配器模式
定义
把一个类的接口转换成对象用户的接口,使得两种不兼容的类可以一起工作。
适配器模式分为类适配器和对象适配器,前者是继承的方式 耦合度高,后者是聚合 组合的方式 耦合度低 。
结构
类适配器(继承)
对象适配器(聚合)
8.装饰器模式
结构
优点
- 扩展性灵活,可以给元对象添加的不同的装饰,组合出多样的结果。遵循开闭原则。
- 装饰者类和被装饰者类(添加别的快餐类型对源代码不影响)可以独立发展,不会相互耦合。
使用场景
-
当不能采用继承的 方式对系统进行扩展,或者继承不利于系统的维护和扩展时
-
- 类中存在大量独立的扩展,为了支持每种组合搭配都需要不同的子类导致类爆炸。
- 类的设计不允许被继承,例如final类
-
不影响其他对象的情况下,对单个对象透明、动态的添加职责。
-
当装饰者的 功能 可以动态的添加或撤销时。
9.桥接模式
结构
优点
- 开闭原则,多个维度(操作系统和图片格式)排列组合方便。
- 实现细节对用户透明
使用场景
- 当一个系统需要两个或者两个以上的维度且维度需要扩展时可以使用桥接模式,并且可以防止继承导致的类爆炸问题。
10.外观模式
结构
优点
- 降低了子系统和客户端之间的耦合度,子系统的变化不影响客户端的调用。
- 对客户屏蔽了子系统组,减少了客户处理的对象数目,使得子系统使用起来更容易。
缺点
不符合开闭原则,修改麻烦。
使用场景
- 当一个复杂系统的子系统很多时,外观模式可以给系统提供一个简单的接口。
- 当客户端和多个复杂的子系统联系复杂时,可以使用外观模式对其整理。
11.组合模式
结构
分类
透明组合模式(标准)
优点:都继承了一个抽象类,便于管理。
缺点:叶子节点不需要add()...方法,所以需要在编码阶段处理
安全组合模式
优点
- 组合模式可以清晰的定义分层级的复杂对象,让客户端忽略了层级的复杂差异,方便管理。
- 可以一致的使用叶子节点和非叶子节点,编码简单。
- 添加节点方便,符合开闭原则。
12.享元模式
定义
通过共享已经存在的对象来大幅度减少要创建的对象的数量,避免不必要的开销。例如共享自行车。
结构
状态
- 内部状态: 不会改变 可以共享。
- 外部状态:不可改变 不能共享。可以根据方法的形参适配。
优点
- 减少内存开销,避免浪费。
- 外部状态(颜色会变化)独立,不影响内部状态(方块很多相同的)。
缺点
内部外部状态需要独立编码,增加了代码的复杂性
行为型模式
13模板方法模式
概述
例如去银行 取号 排队 评分 每个客户都是一样的流程,可以在父类中实现。具体的业务实现可以放到子类中实现。
结构
优点
- 提高代码的复用。
- 实现了反转控制,模板中父类调用了子类实现了的抽象方法。符合开闭原则。
缺点
- 每个不同的实现都需要一个子类,会导致系统庞大。
- 缺点 反向控制 提高了代码阅读的困难。
适用场景
功能中大部分固定,小部分易变。
14 策略模式
结构
优点
- 策略类可以自由切换。
- 易于扩展 添加策略类方便,符合开闭原则。
- 避免使用if else
缺点
- 客户得知道策略类
- 策略类可能会很多,可以用享元模式精简。
使用场景
- 系统需要在几个算法中相互替换
- 一个类中定义了多种行为,并且以分支进行选择。可以用策略模式替换分支。
15命令模式
结构
优点:
- 解耦合:命令者和执行者解耦合;
- 添加删除命令很方便;
- 可以实现宏命令,命令模式和组合模式结合,将多个命令装配成一个组合命令。
- 支持撤销和恢复。
缺点:
- 系统更加复杂
- 可能出现过多的命令类
适用场景:
- 调用者接收者解耦合。
- 可以排队
- 系统需要撤销恢复。
16、责任链模式
结构
优点
- 降低了对象间的耦合度。
- 提高了系统的可扩展性。
- 增强了对象责任的灵活性。
- 简化了对象间的连接。
- 职责分担。
缺点
- 较长的责任链会导致系统的性能下降。
- 增加了客户的复杂性。
17、状态模式
before
问题
- 大量的判断语句导致代码的可读性很差
- 扩展很麻烦