图解23种设计模式

创建型模式:

1、单例模式

单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。
单例模式有 3 个特点:
1、’单例类只有一个实例对象;
2、该单例对象必须由单例类自行创建;
3、单例类对外提供一个访问该单例的全局访问点。
单例模式的结构与实现(太简单不配图)
单例模式是设计模式中最简单的模式之一。通常,普通类的构造函数是公有的,外部类可以通过“new 构造函数()”来生成多个实例。但是,如果将类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。这时该类自身必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例。

//单例模式推荐使用(双重检查)
public class SingletonTest {
    public static void main(String[] args) {
        System.out.println("双重检查");
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2);//true
        System.out.println("instance.hashCode="+instance.hashCode());
        System.out.println("instance2.hashCode="+instance2.hashCode());
    }
}

//懒汉式 线程安全
class Singleton{
    private Singleton(){};//构造方法私有化,别的类就不能去new
    private static volatile Singleton instance; //volatile作用:更新为最新的数据
    //提供一个静态公有方法,加入双重检查代码,解决线程安全问题,同时满足懒加载(用到时才加载)
    public static Singleton getInstance(){
        if (instance == null){
            synchronized (Singleton.class){//synchronized任何线程要执行以下代码,必须获取括号内Singleton类的(锁底层是用该对象的前2个符号表示)

            if (instance==null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}


//饿汉式 (可用)
public class Singleton {
    private final static Singleton INSTANCE = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){
        return INSTANCE;
    }
}

jdk中的Runtime 类,是饿汉式的单例模式,代码如下:

public class Runtime {
    private static Runtime currentRuntime = new Runtime();
    public static Runtime getRuntime() {
        return currentRuntime;
    }
    private Runtime() {}
    
    //手动垃圾回收方法
	public native void gc();
}

为什么要双重检查

1、去掉第一个判断为空:即懒汉式(线程安全),这会导致所有线程在调用getInstance()方法的时候,直接排队等待同步锁,然后等到排到自己的时候进入同步处理时,才去校验实例是否为空,这样子做会耗费很多时间(即线程安全,但效率低下)。

2、去掉第二个判断为空:即懒汉式(线程不安全),这会出现 线程A先执行了getInstance()方法,同时线程B在因为同步锁而在外面等待,等到A线程已经创建出来一个实例出来并且执行完同步处理后,B线程将获得锁并进入同步代码,如果这时B线程不去判断是否已经有一个实例了,然后直接再new一个。这时就会有两个实例对象,即破坏了设计的初衷。(即线程不安全,效率高)

2、工厂模式(简单工厂、工厂方法)

1、简单工厂模式:
举一个客户买车的示例 【客户自己不去new车(不用关心车怎么实现的细节),而是通过静态工厂生产(new)具体车,减少硬编码】
简单工厂模式图:
在这里插入图片描述
新增类型时的图:
在这里插入图片描述

public interface Car{
    void name();
}
public class Bench implements Car{
    @Override
    public void name() {
       System.out.println("奔驰车");
    }
}
public class Baoma implements Car{
    @Override
    public void name() {
        System.out.println("宝马车");
    }  
}
//静态(简单)工厂模式
public class CarFactory {
    public static Car getCar(String car){
        if(car.equals("奔驰"))
            return new Bench();
        else if(car.equals("宝马"))
            return new Baoma();
        else
            return null;
    }
}
public class Customer {
    public static void main(String[] args) {
        Car car = CarFactory.getCar("奔驰");
        car.name();
    }
}

:大多数情况下用的都是简单工厂模式,这种模式仍有不足,如果再增加另一种车,则会修改CarFactory.java的getCar方法,违反了开闭原则,我们应该扩展,不应该修改。

2、工厂方法模式:之前提到了简单工厂模式违背了开闭原则,而“工厂方法模式”是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则

优点:
1、用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
2、灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
3、典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。

缺点:
1、类的个数容易过多,增加复杂度
2、增加了系统的抽象性和理解难度
3、抽象产品只能生产一种产品,此弊端可使用抽象工厂模式解决。

模式的结构与实现
工厂方法模式由抽象工厂、具体工厂、抽象产品和具体产品等4个要素构成。

工厂方法模式的主要角色如下
1、抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品。
2、具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
3、抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
4、具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一 一对应。
在这里插入图片描述

3、抽象工厂模式

抽象工厂(AbstractFactory)模式的定义:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。

抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。

使用抽象工厂模式一般要满足以下条件
1、系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
2、系统一次只可能消费其中某一族产品,即同族的产品一起使用。

抽象工厂模式除了具有工厂方法模式的优点外,其他主要优点如下。
可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。

其缺点是:当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。
在这里插入图片描述
在这里插入图片描述
代码如下:

//抽象产品工厂接口,决定生产哪些产品
public interface IProductFactory {
    //生产手机
    IPhoneProduct phoneProduct();
    //生产路由器
    IRouterProduct routerProduct();
}

//手机接口,决定手机有哪些功能
public interface IPhoneProduct {
    void start();
    void shutdowm();
    void call();
    void sendMassage();
}
//路由器接口
public interface IRouterProduct {
    void start();
    void shutdowm();
    void openWifi();
}
//小米手机
public class HuaweiPhone implements IPhoneProduct{
    @Override
    public void start() {
        System.out.println("华为开机");
    }
    @Override
    public void shutdowm() {
        System.out.println("华为关机");
    }
    @Override
    public void call() {
        System.out.println("华为打电话");
    }
    @Override
    public void sendMassage() {
        System.out.println("华为发信息");
    }
}
//小米手机
public class XiaomiPhone implements IPhoneProduct{
    @Override
    public void start() {
        System.out.println("小米开机");
    }
    @Override
    public void shutdowm() {
        System.out.println("小米关机");
    }
    @Override
    public void call() {
        System.out.println("小米打电话");

    }
    @Override
    public void sendMassage() {
        System.out.println("小米发信息");
    }
}
//华为路由
public class HuaweiRouter implements IRouterProduct {
    @Override
    public void start() {
        System.out.println("华为路由开机");
    }
    @Override
    public void shutdowm() {
        System.out.println("华为路由关机");
    }
    @Override
    public void openWifi() {
        System.out.println("华为路由开Wifi");
    }
}
//小米路由
public class XiaomiRouter implements IRouterProduct {
    @Override
    public void start() {
        System.out.println("小米路由开机");
    }
    @Override
    public void shutdowm() {
        System.out.println("小米路由关机");
    }
    @Override
    public void openWifi() {
        System.out.println("小米路由开Wifi");
    }
}
//华为工厂
public class HuaweiFactory implements IProductFactory {
    @Override
    public IPhoneProduct phoneProduct() {
        return new HuaweiPhone();
    }
    @Override
    public IRouterProduct routerProduct() {
        return new HuaweiRouter();
    }
}
//小米工厂
public class XiaomiFactory implements IProductFactory {
    @Override
    public IPhoneProduct phoneProduct() {
        return new XiaomiPhone();
    }
    @Override
    public IRouterProduct routerProduct() {
        return new XiaomiRouter();
    }
}

工厂模式的总结:一个工厂多种品牌车,多个品牌工厂多种品牌车,多个品牌工厂多种品牌车多种产品(不止有车,还有轮胎,雨刮器等产品),对应3种工厂模式

4、建造者模式

建造者(Builder)模式的定义:指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。

该模式的主要优点如下:
1、封装性好,构建和表示分离。
2、扩展性好,各个具体的建造者相互独立,有利于系统的解耦。
3、客户端不必知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险。

其缺点如下:
1、产品的组成部分必须相同,这限制了其使用范围。
2、如果产品的内部变化复杂,如果产品内部发生变化,则建造者也要同步修改,后期维护成本较大。

建造者(Builder)模式和工厂模式的关注点不同:建造者模式注重零部件的组装过程,而工厂方法模式更注重零部件的创建过程,但两者可以结合使用。

模式的结构
建造者(Builder)模式的主要角色如下:
1、产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个零部件。
2、抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。
3、具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
4、指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。

其结构图如图 所示
在这里插入图片描述
在这里插入图片描述
代码如下

//产品
public class Product {
    private String bulidA;
    private String bulidB;
    private String bulidC;
    private String bulidD;
    public void setBulidA(String bulidA) {
        this.bulidA = bulidA;
    }
    public void setBulidB(String bulidB) {
        this.bulidB = bulidB;
    }
    public void setBulidC(String bulidC) {
        this.bulidC = bulidC;
    }
    public void setBulidD(String bulidD) {
        this.bulidD = bulidD;
    }
    @Override
    public String toString() {
        return "Product{" +
                "bulidA='" + bulidA + '\'' +
                ", bulidB='" + bulidB + '\'' +
                ", bulidC='" + bulidC + '\'' +
                ", bulidD='" + bulidD + '\'' +
                '}';
    }
}
/抽象的建造者:具体需要做哪些方法,得到什么产品
public abstract class Builder {
    abstract void buildA();//地基
    abstract void buildB();//钢筋工程
    abstract void buildC();//铺电线
    abstract void buildD();//粉刷
    //完工:得到产品
    abstract Product getProduct();
}
//具体的建造者 工人,可以多种
public class Worker extends Builder {
    private Product product;
    public Worker() {
        //此处是工人去new产品,不是通过传参
        product = new Product();
    }
    @Override
    void buildA() {
        product.setBulidA("地基");
    }
    @Override
    void buildB() {
        product.setBulidB("钢筋");
    }
    @Override
    void buildC() {
        product.setBulidC("铺电线");
    }
    @Override
    void buildD() {
        product.setBulidD("粉刷");
    }
    @Override
    Product getProduct() {
        return product;
    }
}

//指挥:核心。负责指挥构建一个工程,工程如何构建的顺序等都由它决定
public class Director {
    //指挥工人按自己想要的顺序建房子(以下方法顺序可随自己需要变顺序)
    public Product build(Builder builder){
        builder.buildA();
        builder.buildB();
        builder.buildC();
        builder.buildD();
        return builder.getProduct();
    }
}
//客户测试
public class Client {
    public static void main(String[] args) {
        //指挥
        Director director = new Director();
        //指挥具体的工人完成产品
        Product product = director.build(new Worker());
        System.out.println(product.toString());
    }
}

5、原型模式

原型(Prototype)模式的定义如下:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。。
原型模式的优点:
1、Java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
2、可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
原型模式的缺点:
1、需要为每一个类都配置一个 clone 方法
2、clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
3、当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。
模式的实现
原型模式的克隆分为 浅克隆深克隆
浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址
深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
浅克隆代码

//1、实现一个接口 Cloneable
//2、重写一个方法 clone()
//原型
public class Video implements Cloneable{
    private String name;
    private Date createTime;
    public Video(String name, Date createTime) {
        this.name = name;
        this.createTime = createTime;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        //默认父类的方法浅克隆
        return super.clone();
    }
     public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Video{" +
                "name='" + name + '\'' +
                ", createTime=" + createTime +
                '}';
    }
}

//客户端:浅克隆
public class Bili {
    public static void main(String[] args) throws CloneNotSupportedException {
        //原型对象 v1
        Date date = new Date();
        Video v1 = new Video("huige", date);
        System.out.println("v1=>"+v1);
        System.out.println("v1=>hash:"+v1.hashCode());
       //浅克隆:通过v1克隆出v2,内容和原来一样,但是hashCode不一样是两个不同的实例,
        //创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
        // 即都指向同一个Date空间,修改一个date后两个会一样
        Video v2 = (Video) v1.clone();
        System.out.println("v2=>"+v2);
        System.out.println("v2=>hash:"+v2.hashCode());
        System.out.println("===============================");
        v2.setName("Clone");
        date.setTime(2343443);//Date特有的方法,修改原型v1时间,v2也会变
        System.out.println(v2);
        System.out.println(v1);
    }
}

深度克隆代码

//深克隆
public class Video implements Cloneable{
    private String name;
    private Date createTime;
//深克隆代码主要区别在这个clone函数
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object obj = super.clone();
        Video v=(Video) obj;
        //深克隆:将这个对象的属性也进行克隆
        v.createTime = (Date) this.createTime.clone();
        return obj;
    }
    public Video(String name, Date createTime) {
        this.name = name;
        this.createTime = createTime;
    }

  
    public void setName(String name) {
        this.name = name;
    }
}

//客户端:深克隆
public class Bili {
    public static void main(String[] args) throws CloneNotSupportedException {
        //原型对象 v1
        Date date = new Date();
        Video v1 = new Video("huige", date);
        System.out.println("v1=>"+v1);
        System.out.println("v1=>hash:"+v1.hashCode());

        //深克隆:通过v1克隆出v2,内容和原来一样,但是hashCode不一样是两个不同的实例,
        // 修改内容各自不影响,都指向不同Date空间
        Video v2 = (Video) v1.clone();
        System.out.println("v2=>"+v2);
        System.out.println("v2=>hash:"+v2.hashCode());
        System.out.println("===============================");
        v2.setName("Clone");
        date.setTime(2343443);//Date特有的方法,修改原型v1时间,v2不会变
        System.out.println("v1=>"+v1);
        System.out.println("v2=>"+v2);
    }
}

结构型模式

1、适配器模式

适配器模式(Adapter)的定义如下:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。适配器模式分为类结构型模式对象结构型模式两种,前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。

类和对象结构的区别
1、类适配器的重点在于类,是通过构造一个继承Adaptee类来实现适配器的功能;
2、对象适配器的重点在于对象,是通过在直接包含Adaptee类来实现的,当需要调用特殊功能的时候直接使用Adapter中包含的那个Adaptee对象来调用特殊功能的方法即可。

优点:
1、客户端通过适配器可以透明地调用目标接口。
2、复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类。
3、将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。

对象适配器代码:(常用)
就是转换器调用了网线的功能,电脑再调用转换器,电脑间接调用网线功能

//接口转换器的抽象实现 接口,为了可以造更多类型的转换器
public interface NetToUsb {
    //作用:处理连接请求, 网线=>usb
    public void handleConnet();
}
//要被适配的类:网线(想直接插只要USB的电脑,不行)
public class NetLine {
    public void connet(){
        System.out.println("连接网线上网");
    }
}
//组合NetLine (对象适配器)
public class Adapter implements NetToUsb {
    private NetLine netLine;
    public Adapter(NetLine netLine) {
        this.netLine = netLine;
    }
    @Override
    public void handleConnet() {
        netLine.connet();//可以连接上网了
    }
}
//客户端类:电脑  想上网,插不上网线
public class Computer {
    //传一个转接头过来,我们电脑才可以连上
    public void net(NetToUsb adapter){
        //上网的具体实现,找一个转接头连接
        adapter.handleConnet();
    }
    public static void main(String[] args) {
        //电脑,适配器,网线
        Computer computer = new Computer();
        NetLine netLine = new NetLine();
        Adapter adapter = new Adapter(netLine);//把网线装上
        computer.net(adapter);
    }
}

类适配器:(很少用)

//继承NetLine (类适配器)
public class Adapter2 extends NetLine implements NetToUsb {
    @Override
    public void handleConnet() {
        super.connet();//可以连接上网了
    }
}
//客户端类:电脑  想上网,插不上网线
    //类适配器
public class Computer2 {
    //传一个转接头过来,我们电脑才可以连上
    public void net(NetToUsb adapter){
        //上网的具体实现,找一个转接头
        adapter.handleConnet();
    }
    public static void main(String[] args) {
        //电脑,适配器
        Computer2 computer = new Computer2();
        Adapter2 adapter2 = new Adapter2();
        computer.net(adapter2);
    }

}

2、桥接模式

桥接(Bridge)模式的定义:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。

桥接(Bridge)模式的优点
由于抽象与实现分离,所以扩展能力强;
其实现细节对客户透明。

缺点:由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,这增加了系统的理解与设计难度。

模式的结构桥接(Bridge)模式包含以下主要角色
1、抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。
2、扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
3、实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。
4、具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。
如下图:
在这里插入图片描述

普通多继承类导致的问题:
在这里插入图片描述
为解决上面这个问题引出桥接模式(把上图横向设计再组合就是桥接):
在这里插入图片描述

//品牌 的接口
public interface Brand {
    void info();
}
//苹果品牌
public class Apple implements Brand {
    @Override
    public void info() {
        System.out.print("苹果");
    }
}
public class Lenovo implements Brand {
    @Override
    public void info() {
        System.out.print("联想");
    }
}

//电脑类型的抽象类
public abstract class Computer {
    //把品牌 组合进来  起到桥的作用
    protected Brand brand;//保证子类也能用,把 private 换成 protected
    public Computer(Brand brand) {
        this.brand = brand;
    }
    public void info() {
        //用哪个品牌就调用谁
        brand.info();
    }
}
//台式电脑
class Desktop extends Computer{
    public Desktop(Brand brand) {
        super(brand);
    }
    @Override
    public void info() {
        super.info();
        System.out.println("台式电脑");
    }
}
//笔记本电脑
class Laptop extends Computer{
    public Laptop(Brand brand) {
        super(brand);
    }
    @Override
    public void info() {
        super.info();
        System.out.println("笔记本电脑");
    }
}
//测试
public class Client {
    public static void main(String[] args) {
        //苹果笔记本
        Computer computer = new Laptop(new Apple());
        computer.info();
        //联系台式机
        Computer computer2 = new Desktop(new Lenovo());
        computer2.info();
    }
}

3、代理模式

3.1 静态代理模式

代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

代理模式的主要优点有:

1、代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
2、代理对象可以扩展目标对象的功能;
3、代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性

其主要缺点是:

1、代理模式会造成系统设计中类的数量增加
2、在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
3、增加了系统的复杂度;

代理模式的主要角色如下:

1、抽象角色(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
2、真实角色(Real Subject)类:被代理的角色;实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
3、代理(Proxy)角色:代理真实角色,一般还会有自己的一些附属操作
4、客户:访问代理对象。

如图:
在这里插入图片描述
代码如下

//租房 接口
public interface Rent {
    void rent();
}
public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("房东要出租房子");
    }
}
public class Proxy implements Rent {
    private  Host host;
    public Proxy(Host host) {
        seeHouse();//中介特有的附属操作
        this.host = host;//调用房东租房业务
        fare();//中介特有的附属操作
    }
    @Override
    public void rent() {
        host.rent();
    }
    //中介特有的附属操作
    public void seeHouse(){
        System.out.println("中介带你看房");
    }
    //中介特有的附属操作
    public void fare(){
        System.out.println("中介收费");
    }
}
//测试客户访问代理类
public class Client {
    public static void main(String[] args) {
        //房东要租房子
        Host host = new Host();
        //代理,中介帮房东租房子,但是代理一般会有一些附属操作
        Proxy proxy = new Proxy(host);
        //你不用面对房东,直接找中介租房即可
        proxy.rent();
    }
}

深入理解静态代理的代码

//抽象接口
public interface UserService {
    void add();
    void delete();
    void update();
    void query();
}
//具体实现类,被代理
public class UserServiceImpl implements UserService
{
    @Override
    public void add() {
        //要求:在执行这些方法时,输出日志中要说明使用了这个方法。
        // 即新加一句 System.out.println("使用了add方法");
        //但如果实现类太多,全部的方法内部都要修改(但公司原则又是尽量不改原来的代码),
        // 而且代码很累赘,所以想到使用代理类
        //System.out.println("使用了add方法");
        System.out.println("增加了一个用户");
    }
    @Override
    public void delete() {
        System.out.println("删除了一个用户");
    }
    @Override
    public void update() {
        System.out.println("修改了一个用户");
    }
    @Override
    public void query() {
        System.out.println("查询了一个用户");
    }
}
//代理
public class UserServiceProxy implements UserService {
        private UserServiceImpl userService;

        //springboot 推荐用set注入 而不用构造函数
        public void setUserService(UserServiceImpl userService) {
            this.userService = userService;
        }
        //自己的附属操作:日志方法
        public void log(String mesg){
            System.out.println("使用了"+mesg+"方法");
        }
        @Override
        public void add() {
            log("add");
            userService.add();
        }
        @Override
        public void delete() {
            log("delete");
            userService.delete();
        }
    @Override
    public void update() {
        log("update");
        userService.update();
    }
    @Override
    public void query() {
        log("query");
        userService.query();
    }
}
//客户
public class Client {
    public static void main(String[] args) {
        //不引入代理类时直接访问的代码
       // UserServiceImpl userService = new UserServiceImpl();
        //userService.add();

        //引入代理类时访问代理对象的代码
        UserServiceImpl userService = new UserServiceImpl();
        UserServiceProxy proxy = new UserServiceProxy();
        proxy.setUserService(userService);//传入真实要代理的对象
        proxy.add();
    }
}

3.2 动态代理(角色不变)

java动态代理是Spring AOP 的核心技术。这种动态代理也是jdk的一个特性。
在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。

Proxy:提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。

InvocationHandler:需新建一个类去实现这个接口,配置好要代理的目标后,重写它的invoke方法

动态代理代码:(抽象接口和具体实现类代码与上相同)

//新建 代理类调用程序处理器
// InvocationHandler:调用程序处理器。
// 用这个类自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
    //被代理的接口
    private Object target;
    public void setTarget(Object target) {
        this.target = target;
    }
    
    //Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类
    //Proxy生成得到代理类,需三个参数
    //1、代理类调用程序处理器(即本类)的类加载器
    //2、被代理的(真实对象实现的)接口(自动生成的代理类就会是实现了这个接口);
    //3、实现了InvocationHandler接口的类(即本类)
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);
    }
    //处理代理实例,并返回结果
    //proxy - 调用该方法的代理实例
    //method -所述方法对应于调用代理实例上的接口方法的实例。
    //args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());//动态获得方法名
        //动态代理的本质,就是使用反射机制实现!
        Object result = method.invoke(target, args);
        return result;
    }
    public void log(String mesg){
        System.out.println("执行了"+mesg+"方法");
    }
}
//客户
public class Client {
    public static void main(String[] args) {
        //真实角色
        UserServiceImpl userServiceImpl = new UserServiceImpl();
        //代理角色,现在还没有,
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //设置要代理谁
        pih.setTarget(userServiceImpl);
        //通过调用ProxyInvocationHandler的getProxy()生成一个代理对象,而且是实现了UserService接口的
        UserService proxy = (UserService) pih.getProxy();//这里的 proxy 代理类是被动态生成的

        proxy.add();
    }
}

springAOP 核心就是动态代理模式
在这里插入图片描述

4、装饰模式

装饰(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态的将新功能 附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则。

装饰(Decorator)模式的主要优点有:
采用装饰模式扩展对象的功能比采用继承方式更加灵活。
可以设计出多个不同的具体装饰类,创造出多个不同行为的组合。

其主要缺点:装饰模式增加了许多子类,如果过度使用会使程序变得很复杂。

装饰模式的结构与实现
通常情况下,扩展一个类的功能会使用继承方式来实现。但继承具有静态特征,耦合度高,并且随着扩展功能的增多,子类会很膨胀。如果使用组合关系来创建一个包装对象(即装饰对象)来包裹真实对象,并在保持真实对象的类结构不变的前提下,为其提供额外的功能,这就是装饰模式的目标。

装饰模式主要包含以下角色

抽象构件(Component)角色:定义一个抽象接口或类以规范准备接收附加责任的对象。
具体构件(Concrete Component)角色:实现抽象构件,通过装饰角色为其添加一些职责。
抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

在这里插入图片描述

客户测试代码(其余代码太多不放)

public class Client {
    public static void main(String[] args) {
        //装饰者模式下订单:2份巧克力和一份牛奶的LongBlack 咖啡
        //只要把对象的参数在原来的基础上再传一次就好了
        //1、先点一份LongBlack
        Drink order = new LongBlack();
        //2、加一份牛奶
        order = new Milk(order);
        //3、再原来基础上再加一份巧克力
        order = new Chocolate(order);
        //4、再原来基础上再加另一份巧克力
        order = new Chocolate(order);
    }
}

java IO 中用到的装饰者模式
在这里插入图片描述

public class JavaIO {
    public static void main(String[] args) throws IOException {
        DataInputStream dis = new DataInputStream(new FileInputStream("c:\\a.txt"));
        System.out.println(dis.read());
        dis.close();
    }
}

5、组合模式

组合(Composite)模式的定义:有时又叫作 部分-整体模式,它是一种将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性。

组合模式的主要优点有:

1、组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
2、更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;

其主要缺点是:

1、设计较复杂,客户端需要花更多时间理清类之间的层次关系;
2、不容易限制容器中的构件;
3、不容易用继承的方法来增加构件的新功能;

组合模式包含以下主要角色。

抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。
树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于实现抽象构件角色中 声明的公共接口。
树枝构件(Composite)角色:是组合中的分支节点对象,它有子节点。它实现了抽象构件角色中声明的接口,它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。

例子(一个大学包含很多学院,一个学院包含多种专业)
在这里插入图片描述
Java HashMap 应用的组合模式分析图:

在这里插入图片描述
在这里插入图片描述

6、外观模式

外观(Facade)模式定义:外观模式又叫作门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。

通常只要是高层模块需要调度多个子系统,我们都会自觉地创建一个新的类封装这些子系统,提供精简的接口,让高层模块可以更加容易地间接调用这些子系统的功能。尤其是现阶段各种第三方SDK、开源类库,很大概率都会使用外观模式。

外观(Facade)模式是“迪米特法则”的典型应用,它有以下主要优点:

1、降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。
2、对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
3、降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。

外观(Facade)模式的主要缺点如下。

1、不能很好地限制客户使用子系统类,很容易带来未知风险。
2、增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。

外观模式的角色

  1. 外观类(Facade): 为调用端提供统一的调用接口, 外观类知道哪些子系统负责处理请求,从而将调用端的请求代理给适当子系统对象
  2. 调用者(Client): 外观接口的调用者
  3. 子系统的集合:指模块或者子系统,处理Facade 对象指派的任务,他是功能的实际提供者

在这里插入图片描述

子系统内部也可存在复杂调用关系
在这里插入图片描述
myBatis中外观模式源码分析
在这里插入图片描述
上图的角色分析
在这里插入图片描述

7、享元模式

享元(Flyweight)模式的定义:运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。

享元模式的主要优点是:相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。

其主要缺点是:为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
读取享元模式的外部状态会使得运行时间稍微变长。

享元模式中存在以下两种状态:
内部状态,即不会随着环境的改变而改变的可共享部分;
外部状态,指随环境改变而改变的不可以共享的部分。享元模式的实现要领就是区分应用中的这两种状态,并将外部状态外部化。

享元模式的主要角色有如下:
1、抽象享元角色(Flyweight):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。
2、具体享元(Concrete Flyweight)角色:实现抽象享元角色中所规定的接口。
3、非享元(Unsharable Flyweight)角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。
4、享元工厂(Flyweight Factory)角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。

享元模式基本介绍

  1. 享元模式(Flyweight Pattern) 也叫 蝇量模式: 运
    用共享技术有效地支持大量细粒度的对象
  2. 常用于系统底层开发,解决系统的性能问题。像
    数据库连接池,里面都是创建好的连接对象,在
    这些连接对象中有我们需要的则直接拿来用,避
    免重新创建,如果没有我们需要的,则创建一个
  3. 享元模式能够解决 重复对象的内存浪费的问题,
    当系统中有大量相似对象,需要缓冲池时。不需
    总是创建新对象,可以从缓冲池里拿。这样可以
    降低系统内存,同时提高效率
  4. 享元模式 经典的应用场景就是池技术了,String常
    量池、数据库连接池、缓冲池等等都是享元模式
    的应用,享元模式是池技术的重要实现方式

业务要求
给客户A做一个产品展示网站,客户A的朋友感觉效果不错,也希
望做这样的产品展示网站,但是要求都有些不同:

  1. 有客户要求以新闻的形式发布
  2. 有客户人要求以博客的形式发布
  3. 有客户希望以微信公众号的形式发布
    在这里插入图片描述代码如下
//抽象享元
public abstract class WebSite {
    public  abstract void use(User user);
}
//具体享元
public class ConcareteWebSite extends WebSite {
    //共享的部分,内部状态,同一种type就会被共享(比如都是博客类型)
    private String type = "";//网站发布的形式(类型)

    public ConcareteWebSite(String type) {
        this.type = type;
    }
    @Override
    public void use(User user ) {
        System.out.println("网站的发布形式为:"+type+"  使用者:"+user.getName());
    }
}
//网站工厂类,根据需求返回一个具体网站类型
public class WebSiteFactory {
    //集合,充当池使用
    private HashMap<String,ConcareteWebSite> pool = new HashMap<>();

    //根据网站类型返回一个网站,如果没有就创建一个,放入池中,并返回
    public WebSite getWebSiteCategory(String type){
        if (!pool.containsKey(type)){
            pool.put(type,new ConcareteWebSite(type));
        }
        return (WebSite) pool.get(type);
    }
    //获取网站类型的总数
    public int getWebSiteCount(){
        return pool.size();
    }
}
//非享元类
public class User {
    private String name;
    public User(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}
//客户测试
public class Client {
    public static void main(String[] args) {
        //创建一个工厂
        WebSiteFactory factory = new WebSiteFactory();
        //客户要一个新闻形式发布的网站
        WebSite webSite1 = factory.getWebSiteCategory("新闻");
        webSite1.use(new User("tom"));
        //客户要一个博客形式发布的网站
        WebSite webSite2 = factory.getWebSiteCategory("博客");
        webSite1.use(new User("moti"));
        //客户要一个博客形式发布的网站,已经创建过了,去工厂池共享拿来就行
        WebSite webSite3 = factory.getWebSiteCategory("博客");
        webSite1.use(new User("kiti"));
        //输出创建的网站类型个数
        System.out.println(factory.getWebSiteCount());//2
    }
}

String池 享元原理(虽然引用不同,但拿到的是同一个hello对象)
在这里插入图片描述
valueOf源码:(low为-128,high为127)

在这里插入图片描述valueOf 和String池 享元模式源码测试:

public class JdkInteger {
    public static void main(String[] args) {
        Integer x = Integer.valueOf(127);
        Integer y = Integer.valueOf(127);
        Integer z = new Integer(127);
        Integer k = new Integer(127);

        System.out.println(x.equals(z));//true  因为比较的是数值大小
        System.out.println(x==y);//true 因为源码里数值-128到127内,如果创建过对象,会共享,是同个对象
        System.out.println(x==z);//false
        System.out.println(z==k);//false

        Integer a = Integer.valueOf(128);
        Integer b = Integer.valueOf(128);
        //源码中超过127 就new一个对象,所以他们不是用一个
        System.out.println(a==b);//false 

        String s = "hello";
        String str = new String("hello");
        //hashCode 通过将对象的内部地址(String池里的hello对象)转换为整数。
        System.out.println(s.hashCode()==str.hashCode());//true
        System.out.println(s==str);//false
    }
}

行为型模式

1、模板方法模式

模板方法(Template Method)模式的定义:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。它是一种类行为型模式。

该模式的主要优点

1、它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
2、它在父类中提取了公共的部分代码,便于代码复用。
部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。

该模式的主要缺点:

1、对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。
2、父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。

模式的结构
(1)抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。这些方法的定义如下。
① 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
② 基本方法:是整个算法中的一个步骤,包含以下几种类型。

抽象方法:在抽象类中申明,由具体子类实现。
具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。
钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。

(2) 具体子类(Concrete Class ):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的一个组成步骤。
在这里插入图片描述

编写制作豆浆的程序,说明如下:

  1. 制作豆浆的流程 选材—>添加配料—>浸泡—>放到豆浆机打碎
  2. 通过添加不同的配料,可以制作出不同口味的豆浆
  3. 选材、浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样的
  4. 请使用 模板方法模式 完成

豆浆类图分析
在这里插入图片描述

//抽象类,豆浆
public abstract class SoyaMilk {
    //模板方法,make , 模板方法可以做成final , 不让子类去覆盖
    final void make(){
        select();
        addCondiments();
        soak();
        beat();
    }
    //选材料,大家都一样要选的
    void select(){
        System.out.println("第一步: 都需要选黄豆");
    }
    //添加不同配料,抽象方法,让子类具体实现(因为每种不同豆浆配料不同)
    abstract void addCondiments();
    
    //浸泡
    void soak(){
        System.out.println("第三步:黄豆和配料开始浸泡");
    }
    //打碎
    void beat(){
        System.out.println("第四步:黄豆和配料去打碎");
    }

}
//红豆豆浆
public class ReadBeanSoyaMilk extends SoyaMilk {
    @Override
    void addCondiments() {
        System.out.println("第二步:加入配料红豆");
    }
}
//花生豆浆
public class PeanutSoyaMilk extends SoyaMilk {
    @Override
    void addCondiments() {
        System.out.println("第二步:加入配料花生");
    }
}
//客户测试
public class Client {
    public static void main(String[] args) {
        //制作红豆豆浆
        System.out.println("------制作红豆豆浆------");
        SoyaMilk readBeanSoyaMilk = new ReadBeanSoyaMilk();
        readBeanSoyaMilk.make();
        //制作花生豆浆
        System.out.println("------制作花生豆浆------");
        SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk();
        peanutSoyaMilk.make();
    }
}

模板方法模式的钩子方法

  1. 在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以
    视情况要不要覆盖它,该方法称为“钩子”。
  2. 还是用上面做豆浆的例子来讲解,比如,我们还希望制作纯豆浆,不添加任何的配
    料,请使用钩子方法对前面的模板方法进行改造

纯豆浆不需要加配料怎么办(钩子函数)

//抽象类,豆浆
public abstract class SoyaMilk {
    //模板方法,make , 模板方法可以做成final , 不让子类去覆盖
    final void make(){
        select();
        //通过判断钩子函数确定要不要添加配料
        if (customerWantCondiments()){
            addCondiments();
        }
        soak();
        beat();
    }
    //选材料,大家都一样要选的
    void select(){
        System.out.println("第一步: 都需要选黄豆");
    }
    //有的子类不需要,让子类空实现
    abstract void addCondiments();
    //浸泡
    void soak(){
        System.out.println("第三步:黄豆和配料开始浸泡");
    }
    //打碎
    void beat(){
        System.out.println("第四步:黄豆和配料去打碎");
    }
    //钩子方法,决定是否需要添加配料,默认true,子类不需要配料时,重写为false即可
    boolean customerWantCondiments(){
        return true;
    }
}
//纯豆浆
public class PureSoyaMilk extends SoyaMilk {
    @Override
    void addCondiments() {
        //不加配料,空实现
    }
    //重写钩子函数,改为 false
    @Override
    boolean customerWantCondiments() {
        return false;
    }
}
//客户测试
public class Client {
    public static void main(String[] args) {
        //制作红豆豆浆
        System.out.println("------制作红豆豆浆------");
        SoyaMilk readBeanSoyaMilk = new ReadBeanSoyaMilk();
        readBeanSoyaMilk.make();
        //制作花生豆浆
        System.out.println("------制作花生豆浆------");
        SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk();
        peanutSoyaMilk.make();
        //制作纯豆浆
        System.out.println("------制作纯豆浆------");
        SoyaMilk pureSoyaMilk = new PureSoyaMilk();
        pureSoyaMilk.make();
    }
}

springIOC容器初始化时用到 模板方法模式 源码分析的类图
在这里插入图片描述

2、命令模式

命令(Command)模式的定义:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。

命令模式的主要优点

1、降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦。
2、增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。
3、可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
4、方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。

其缺点是:可能产生大量具体命令类。因为计对每一个具体操作都需要设计一个具体命令类,这将增加系统的复杂性。

模式角色:

抽象命令类(Command)角色:声明执行命令的接口,拥有执行命令的抽象方法 execute()。
具体命令角色(Concrete Command)角色:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
实现者/接收者(Receiver)角色:执行命令功能的相关操作,是具体命令对象业务的真正实现者。
调用者/请求者(Invoker)角色:是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。

类图如下
在这里插入图片描述

我们买了一套智能家电,有照明灯、风扇、冰箱、洗衣机,我们只要在手机上安装app就
可以控制对这些家电工作。
(代码太多不贴)
在这里插入图片描述命令模式在Spring 框架JdbcTemplate 也有应用

3、访问者模式

访问者(Visitor)模式的定义:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。

访问者(Visitor)模式主要优点如下

1、扩展性好。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
2、复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
3、灵活性好。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。
4、符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。

访问者(Visitor)模式的主要缺点如下:

1、增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
2、破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。
3、违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。

访问者模式包含以下主要角色:

1、抽象访问者(Visitor)角色:定义一个访问具体元素的接口或类,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。
2、具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
3、抽象元素(Element)角色:声明一个包含接受操作 accept() 的接口或类,被接受的访问者对象作为 accept() 方法的参数。
4、具体元素(ConcreteElement)角色:实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。
5、对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。

角色分析图如下:
在这里插入图片描述
题目:将观众分为男人和女人,对歌手进行测评,当看完某个歌手表演后,得到他们对该歌手不同的评价(评价 有不同的种类,比如 成功、失败 等)

分析得类图:
在这里插入图片描述

//抽象访问者
public abstract class Action {
    //得到男性 测评
    public abstract void getManResult(Man man);
    //得到女性 测评
    public abstract void getWomanResult(Woman man);
}
//抽象元素
public  abstract class Person {
    //提供一个方法 让访问者可以访问
    public abstract void accpt(Action action);
}
//这里用到双分派:将具体操作动作action接收进来,再通过action调用方法将自己作为参数(this)传给getManResult
public class Man extends Person {
    @Override
    public void accpt(Action action) {
     action.getManResult(this);
    }
}
//具体访问元素
public class Woman extends Person {
    @Override
    public void accpt(Action action) {
        action.getWomanResult(this);
    }
}
//具体访问者
public class Success extends Action {
    @Override
    public void getManResult(Man man) {
        System.out.println("男观众评价该歌手成功");
    }

    @Override
    public void getWomanResult(Woman man) {
        System.out.println("女观众评价该歌手成功");
    }
}
//具体访问者
public class Fail extends Action {
    @Override
    public void getManResult(Man man) {
        System.out.println("男评委评价该歌手失败");
    }
    @Override
    public void getWomanResult(Woman man) {
        System.out.println("女评委评价该歌手失败");
    }
}
//数据结构,管理很多观众
public class ObjectStructure {
    //维护了一个集合
    private List<Person> people = new LinkedList<>();
    //增加到list
    public void attach(Person p){
        people.add(p);
    }
    //删除
    public void detach(Person p){
        people.remove(p);
    }
    //显示测评情况
    public void display(Action action){
        for (Person p:people) {
            p.accpt(action);
        }
    }
}
//客户测试
public class Client {
    public static void main(String[] args) {
        //创建ObjectStructure
        ObjectStructure objectStructure = new ObjectStructure();
        objectStructure.attach(new Man());
        objectStructure.attach(new Woman());
        //成功
        Success success = new Success();
        objectStructure.display(success);
    }
}

4、迭代器模式

迭代器(Iterator)模式的定义:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。迭代器模式是一种对象行为型模式。

主要优点
1、访问一个聚合对象的内容而无须暴露它的内部表示。
2、遍历任务交由迭代器完成,这简化了聚合类。
3、它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。
4、增加新的聚合类和迭代器类都很方便,无须修改原有代码。
5、封装性良好,为遍历不同的聚合结构提供一个统一的接口。

主要缺点
增加了类的个数,这在一定程度上增加了系统的复杂性。

迭代器模式主要包含以下角色:

1、抽象聚合(Aggregate)角色:定义存储、添加、删除聚合对象以及创建迭代器对象的接口。
2、具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
3、抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。
4、具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。

角色分析图:
在这里插入图片描述
类比上图:(两个抽象类的具体实现类是一一对应的,College多种子类可以有各自存放数据的方式,所以每种数据存储方式对应一种遍历方式,具体遍历代码在Iterator的子类中写)
在这里插入图片描述

JDK中ArrayList源码分析:(大部分集合类都有实现迭代器)
在这里插入图片描述
ArrayList源码分析类图:
在这里插入图片描述

5、观察者模式

观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。

主要优点:

  • 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。
  • 目标与观察者之间建立了一套触发机制。

它的主要缺点:

  • 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。-
  • 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。

主要角色。
1、抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
2、具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
3、抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
4、具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。

角色例子类图:
在这里插入图片描述
Jdk 的Observable 类就使用了观察者模式
角色分析:
在这里插入图片描述

  1. 模式角色分析
  • Observable 的作用和地位等价于 我过 们前面讲过Subject
  • Observable 是类,不是接口,类中已经实现了核心的方法 , 即管理Observer
    法 的方法 add… delete … notify…
  • Observer 的作用和地位等价于我们前面讲过的 Observer, 有update
  • Observable 和 和 Observer 的使用方法和前面讲过的一样,只是Observable 是 是
    类,通过继承来实现观察者模式

6、中介者模式

7、备忘录模式

8、解释器模式

9、状态者模式

10、策略者模式

11、职责链者模式

持续更新中…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值