设计模式

2022/5/4 6:19:22

本文主要是介绍设计模式,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

1 设计模式概述

1.1 GoF(Gang of Four) 23

创建型模式

  • 工厂模式(Factory Pattern)
  • 抽象工厂模式(Abstract Factory Pattern)
  • 单例模式(Singleton Pattern)
  • 建造者模式(Builder Pattern)
  • 原型模式(Prototype Pattern)

结构型模式

  • 适配器模式(Adapter Pattern)
  • 桥接模式(Bridge Pattern)
  • 过滤器模式(Filter、Criteria Pattern)
  • 组合模式(Composite Pattern)
  • 装饰器模式(Decorator Pattern)
  • 外观模式(Facade Pattern)
  • 享元模式(Flyweight Pattern)
  • 代理模式(Proxy Pattern)

行为型模式

  • 责任链模式(Chain of Responsibility Pattern)
  • 命令模式(Command Pattern)
  • 解释器模式(Interpreter Pattern)
  • 迭代器模式(Iterator Pattern)
  • 中介者模式(Mediator Pattern)
  • 备忘录模式(Memento Pattern)
  • 观察者模式(Observer Pattern)
  • 状态模式(State Pattern)
  • 空对象模式(Null Object Pattern)
  • 策略模式(Strategy Pattern)
  • 模板模式(Template Pattern)
  • 访问者模式(Visitor Pattern)

设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。

正确使用设计模式具有以下优点

  • 可以提高程序员的思维能力、编程能力和设计能力
  • 使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期
  • 使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。

1.2 OOP 七大原则

  • 开闭原则:对扩展开放,对修改关闭
  • 里氏替换原则: 继承必须确保超类所拥有的性质在子类中仍然成立
  • 依赖倒置原则: 要面向接口编程,不要面向实现编程
  • 单一职责原则: 控制类的粒度大小、将对象解耦、提高其内聚性
  • 接口隔离原则: 要为各个类建立它们需要的专用接口
  • 迪米特法则: 只与你的直接朋友交谈,不跟“陌生人”说话
  • 合成复用原则: 尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现

单例模式(创建型)

介绍

意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

主要解决:一个全局使用的类频繁地创建与销毁。

何时使用:当您想控制实例数目,节省系统资源的时候。

如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

关键代码:构造函数是私有的。

饿汉式

package com.lee;

// 饿汉式单例
public class Hungry {

    private Hungry() {

    }

    private final static Hungry HUNGRY = new Hungry();

    public static Hungry getInstance() {
        return HUNGRY;
    }
	/*
    	可能会浪费空间
    	
        private byte[] data1 = new byte[1024 * 1024];
        private byte[] data2 = new byte[1024 * 1024];
        private byte[] data3 = new byte[1024 * 1024];
        private byte[] data4 = new byte[1024 * 1024];
    */
}

懒汉式

package com.lee;

// 懒汉式单例
public class LazyMan {

    private static boolean password = false;

    private LazyMan() {

//        synchronized (LazyMan.class) {
//            if (lazyMan != null) {
//                throw new RuntimeException("不要试图使用反射破坏 异常");
//            }
//        }

        synchronized (LazyMan.class) {
            if (password == false) {
                password = true;
            } else {
                throw new RuntimeException("不要试图使用反射破坏 异常");
            }
        }
    }

    private static LazyMan lazyMan;

//    public static LazyMan getInstance() {
//        if (lazyMan == null) {
//            lazyMan = new LazyMan();
//        }
//        return lazyMan;
//    }

    
    // 双重检测锁模式的懒汉式单例    DCL 懒汉式单例
    public static LazyMan getInstance() {
        if (lazyMan == null) {
            synchronized (LazyMan.class) {
                if (lazyMan == null) {
                    lazyMan = new LazyMan();
                    
                    /*
                        lazyMan = new LazyMan(); 不是原子性的操作
                        1. 分配内存空间
                        2. 执行构造方法,初始化对象
                        3. 把这个对象指向这个空间

                        期望执行顺序 123
                        实际执行可能 132 ...
                     */
                }
            }
        }
        return lazyMan;
    }

    // 只适用于单线程

    // 多线程并发
    public static void main(String[] args) {

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                LazyMan.getInstance();
            }).start();
        }
    }
}

class TestLazyMan {

    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {

//        LazyMan instance = LazyMan.getInstance();
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        // 可获得 private 属性方法
        declaredConstructor.setAccessible(true);
        LazyMan instance2 = declaredConstructor.newInstance();
        LazyMan instance3 = declaredConstructor.newInstance();
//        System.out.println(instance == instance2);
        System.out.println(instance2 == instance3);
        
    }
}

单例不安全,可以被反射破坏

静态内部类

package com.lee;

// 静态内部类
public class Holder {
    private Holder() {
    }
    public static Holder getInstance() {
        return InnerClass.HOLDER;
    }

    public static class InnerClass {
        private static final Holder HOLDER = new Holder();
    }
}

枚举

枚举单例不可破坏

package com.lee;


import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

// 枚举
public enum EnumSingle {

    INSTANCE;
    public EnumSingle getInstance() {
        return INSTANCE;
    }
}

class TestEnum {

    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {

        EnumSingle instance1 = EnumSingle.INSTANCE;

        //Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
        //  java.lang.NoSuchMethodException: com.lee.EnumSingle.<init>()

        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class);
        // Cannot reflectively create enum objects
        // at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
        // 得出结论: 枚举单例不可破坏

        declaredConstructor.setAccessible(true);
        EnumSingle instance3 = declaredConstructor.newInstance();
        EnumSingle instance2 = EnumSingle.INSTANCE;

        System.out.println(instance1);
        System.out.println(instance2);
        System.out.println(instance3);
    }
}

工厂模式(创建型)

  • 简单工厂模式
    • 用来生产同一等级结构中的任意产品(对于增加新的产品,需要扩展已有代码)
  • 工厂方法模式
    • 用来生产同一等级结构中的固定产品(支持增加任意产品)
  • 抽象工厂模式
    • 围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂

接口 car

public interface Car {
    void name();
}

实现类一

public class WuLing implements Car{

    @Override
    public void name() {
        System.out.println("五菱宏光");
    }
}

实现类二

public class Tesla implements Car{
    @Override
    public void name() {
        System.out.println("特斯拉");
    }
}

简单工厂

public class CarFactory {

    public static Car getCar(String car) {
        if (car.equals("五菱")) {
            return new WuLing();
        } else if (car.equals("特斯拉")) {
            return new Tesla();
        } else {
            return null;
        }
    }
}
public class Consumer {

    public static void main(String[] args) {


        Car wuLing = CarFactory.getCar("五菱");
        Car tesla = CarFactory.getCar("特斯拉");

        wuLing.name();
        tesla.name();
    }
}

应用举例

  • JDK中Calendar的getlnstance方法
  • JDBC中的Connection对象的获取
  • Spring中IOC容器创建管理bean对象
  • 反射中Class对象的newlnstance方法

工厂方法

WuLingFactory

public class WuLingFactory implements CarFactory{
    @Override
    public Car getCar() {
        return new WuLing();
    }
}

TeslaFactory

public class TeslaFactory implements CarFactory{
    @Override
    public Car getCar() {
        return new Tesla();
    }
}
// 工厂方法模式
public interface CarFactory {
    Car getCar();
}
public class Consumer {

    public static void main(String[] args) {

        Car wuLing = new WuLingFactory().getCar();
        Car tesla = new TeslaFactory().getCar();
        wuLing.name();
        tesla.name();

        Car mobai = new MobaiFactory().getCar();
        mobai.name();


    }
}

结构复杂度、代码复杂度、编程复杂度、管理上的复杂度 都是简单工厂好一些

  • 根据设计原则:工厂方法模式
  • 根据实际业务:简单工厂模式

抽象工厂

image46

// 超级工厂
public interface IProductFactory {
    // 生产手机
    IphoneProduct phoneProduct();
    // 生产路由器
    IRouterProduct RouterProduct();
}

// 手机产品
public interface IphoneProduct {

    void start();
    void shutdown();
    void callup();
    void sengMS();
}

// 路由器产品
public interface IRouterProduct {
    void start();
    void shutdown();
    void openWifi();
    void setting();
}

// MiFactory
public class MiFactory implements IProductFactory{
    @Override
    public IphoneProduct phoneProduct() {
        return new MiPhone();
    }

    @Override
    public IRouterProduct RouterProduct() {
        return new MiRouter();
    }
}

public class HWFactory implements IProductFactory{
    @Override
    public IphoneProduct phoneProduct() {
        return new HWPhone();
    }
    @Override
    public IRouterProduct RouterProduct() {
        return new HWRouter();
    }
}

具体产品

// MiPhone
public class MiPhone implements IphoneProduct{
    @Override
    public void start() {
        System.out.println("小米手机开机");
    }

    @Override
    public void shutdown() {
        System.out.println("小米手机关机");
    }

    @Override
    public void callup() {
        System.out.println("小米手机打电话");
    }

    @Override
    public void sengMS() {
        System.out.println("小米手机发短信");
    }
}

// MiRouter
public class MiRouter implements IRouterProduct{
    @Override
    public void start() {
        System.out.println("小米路由器开启");
    }

    @Override
    public void shutdown() {
        System.out.println("小米路由器关闭");
    }

    @Override
    public void openWifi() {
        System.out.println("小米路由器打开wifi");
    }

    @Override
    public void setting() {
        System.out.println("小米路由器设置");
    }
}
// HWPhone
public class HWPhone implements IphoneProduct{
    @Override
    public void start() {
        System.out.println("华为手机开机");
    }

    @Override
    public void shutdown() {
        System.out.println("华为手机关机");
    }

    @Override
    public void callup() {
        System.out.println("华为手机打电话");
    }

    @Override
    public void sengMS() {
        System.out.println("华为手机发短信");
    }
}

// public class HWRouter implements IRouterProduct{
    @Override
    public void start() {
        System.out.println("华为路由器开启");
    }

    @Override
    public void shutdown() {
        System.out.println("华为路由器关闭");
    }

    @Override
    public void openWifi() {
        System.out.println("华为路由器打开wifi");
    }

    @Override
    public void setting() {
        System.out.println("华为路由器设置");
    }
}

客户端

public class Client {

    public static void main(String[] args) {

//        MiFactory factory = new MiFactory();
//        HWFactory factory = new HWFactory();

//        IProductFactory factory = new HWFactory();
        IProductFactory factory = new MiFactory();

        IphoneProduct phoneProduct = factory.phoneProduct();
        IRouterProduct routerProduct = factory.RouterProduct();

        phoneProduct.sengMS();
        phoneProduct.callup();

        routerProduct.openWifi();
        routerProduct.setting();
    }
}

建造者模式(创建型)

  • 建造者模式也属于创建型模式,它提供了一种创建对象的最佳方式
  • 定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
  • 主要作用:在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象
  • 用户只需要给出指定复杂对象的类型和内容,建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)

案例一

image-20220426172552723

package com.lee.demo01;

public abstract class Builder {

    // 步骤 A
    abstract void builderA();
    // 步骤 B
    abstract void builderB();
    // 步骤 C
    abstract void builderC();
    // 步骤 D
    abstract void builderD();

    // 完工, 得到产品
    abstract Product getProduct();


}
package com.lee.demo01;

// 产品
public class Product {

    private String buildA;
    private String buildB;
    private String buildC;
    private String buildD;

    public String getBuildA() {return buildA;}
    public void setBuildA(String buildA) {this.buildA = buildA;}
    public String getBuildB() {return buildB;}
    public void setBuildB(String buildB) {this.buildB = buildB;}
    public String getBuildC() {return buildC;}
    public void setBuildC(String buildC) {this.buildC = buildC;}
    public String getBuildD() {return buildD; }
    public void setBuildD(String buildD) {this.buildD = buildD;}

    @Override
    public String toString() {
        return "Product{" +
                "buildA='" + buildA + '\'' +
                ", buildB='" + buildB + '\'' +
                ", buildC='" + buildC + '\'' +
                ", buildD='" + buildD + '\'' +
                '}';
    }
}
package com.lee.demo01;

// 具体的建造者--工人
public class Worker extends Builder{

    private Product product;
    public Worker() {
        product = new Product();

    }
    @Override
    void builderA() {
        System.out.println("工人进行步骤A...");
        product.setBuildA("步骤A完成...");
    }

    @Override
    void builderB() {
        System.out.println("工人进行步骤B...");
        product.setBuildB("步骤B完成...");
    }

    @Override
    void builderC() {
        System.out.println("工人进行步骤C...");
        product.setBuildC("步骤C完成...");
    }

    @Override
    void builderD() {
        System.out.println("工人进行步骤D...");
        product.setBuildD("步骤D完成...");
    }

    @Override
    Product getProduct() {
        return product;
    }
}

private Product product; public Worker() { product = new Product();}

// 指挥, 核心 负责指挥构建一个工程, 工程如何构建
public class Director {

    // 指挥工按顺序完工
    public Product build(Builder builder) {
        builder.builderA();
        builder.builderB();
        builder.builderC();
        builder.builderD();

        return builder.getProduct();
    }
}
public class Test {
    public static void main(String[] args) {
        // 指挥
        Director director =new Director();
        // 指挥工人完成产品
        Product build = director.build(new Worker());
        System.out.println(build.toString());
    }
}

案例二

image-20220426173310050

public abstract class Builder {

    // 汉堡
    abstract Builder builderA(String msg);
    // 可乐
    abstract Builder builderB(String msg);
    // 薯条
    abstract Builder builderC(String msg);
    // 甜点
    abstract Builder builderD(String msg);

    abstract Product getProduct();
}
public class Product {

    private String buildA = "汉堡";
    private String buildB = "可乐";
    private String buildC = "薯条";
    private String buildD = "甜点";

    public String getBuildA() {
        return buildA;
    }

    public void setBuildA(String buildA) {
        this.buildA = buildA;
    }

    public String getBuildB() {
        return buildB;
    }

    public void setBuildB(String buildB) {
        this.buildB = buildB;
    }

    public String getBuildC() {
        return buildC;
    }

    public void setBuildC(String buildC) {
        this.buildC = buildC;
    }

    public String getBuildD() {
        return buildD;
    }

    public void setBuildD(String buildD) {
        this.buildD = buildD;
    }

    @Override
    public String toString() {
        return "Product{" +
                "buildA='" + buildA + '\'' +
                ", buildB='" + buildB + '\'' +
                ", buildC='" + buildC + '\'' +
                ", buildD='" + buildD + '\'' +
                '}';
    }
}
// 具体的建造者
public class Worker extends Builder {

    private Product product;
    public Worker() {
        product = new Product();
    }
    @Override
    Builder builderA(String msg) {
        product.setBuildA(msg);
        return this;
    }

    @Override
    Builder builderB(String msg) {
        product.setBuildB(msg);
        return this;
    }

    @Override
    Builder builderC(String msg) {
        product.setBuildC(msg);
        return this;
    }

    @Override
    Builder builderD(String msg) {
        product.setBuildD(msg);
        return this;
    }

    @Override
    Product getProduct() {
        return product;
    }
}

private Product product; public Worker() { product = new Product();}

public class Test {
    public static void main(String[] args) {
        // 服务员
        Worker worker = new Worker();
//        Product product = worker.getProduct();
        Product product = worker.builderA("全家桶").builderB("雪碧").getProduct();
        System.out.println(product.toString());
    }
}

Product product = worker.builderA("全家桶").builderB("雪碧").getProduct();

  • 优点:
    • 产品的建造和表示分离,实现了解耦。使用建造者模式可以使客户端不必知道产品内部组成的细节
    • 将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰
    • 具体的建造者类之间是相互独立的,这有利于系统的扩展。增加新的具体建造者无需修改原有类库的代码,符合开闭原则
  • 缺点:
    • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
    • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

应用场景

  • 需要生成的产品对象有复杂的内部结构,这些产品对象具备共性;
  • 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
  • 适合于一个具有较多的零件(属性)的产品(对象)的创建过程。

建造者与抽象工厂模式的比较

  • 与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族
  • 在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整的对象。
  • 如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车

原型模式(创建型)

案例一

package com.lee.demo01;

import java.util.Date;

/*
    1. 实现一个接口  Cloneable
    2. 重写一个方法  clone()
    3. 无参构造、有参构造
    4. getter setter
 */

public class Video implements Cloneable {

    private String name;
    private Date createTime;


    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public Video() {
    }

    public Video(String name, Date createTime) {
        this.name = name;
        this.createTime = createTime;
    }

    public String getName() {
        return name;
    }

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

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    @Override
    public String toString() {
        return "Video{" +
                "name='" + name + '\'' +
                ", createTime=" + createTime +
                '}';
    }
}
package com.lee.demo01;


import java.util.Date;

public class Bili {

    public static void main(String[] args) throws CloneNotSupportedException {
        Date date = new Date();
        Video video = new Video("Kite Study Java", date);

        System.out.println(video);
        System.out.println(video.hashCode());


        // 浅克隆
        Video video_c = (Video) video.clone();

        System.out.println(video_c);
        System.out.println(video_c.hashCode());

        System.out.println("==========");

        date.setTime(220002020);
        System.out.println(video);
        System.out.println(video_c);

        System.out.println("==========");

        date = new Date();
        video.setCreateTime(date);

        System.out.println(video);
        System.out.println(video_c);
    }
}

输出结果

Video{name='Kite Study Java', createTime=Tue Apr 26 20:17:50 CST 2022}
1735600054
Video{name='Kite Study Java', createTime=Tue Apr 26 20:17:50 CST 2022}
21685669
==========
Video{name='Kite Study Java', createTime=Sat Jan 03 21:06:42 CST 1970}
Video{name='Kite Study Java', createTime=Sat Jan 03 21:06:42 CST 1970}
==========
Video{name='Kite Study Java', createTime=Tue Apr 26 20:17:50 CST 2022}
Video{name='Kite Study Java', createTime=Sat Jan 03 21:06:42 CST 1970}

案例二

package com.lee.demo02;

import java.util.Date;


public class Video implements Cloneable {

    private String name;
    private Date createTime;


    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object obj = super.clone();
        Video v = (Video) obj;
        v.createTime = (Date) this.createTime.clone();

        return v;
    }

    public Video() {
    }

    public Video(String name, Date createTime) {
        this.name = name;
        this.createTime = createTime;
    }

    public String getName() {
        return name;
    }

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

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    @Override
    public String toString() {
        return "Video{" +
                "name='" + name + '\'' +
                ", createTime=" + createTime +
                '}';
    }
}
package com.lee.demo02;

import java.util.Date;

public class Bili {

    public static void main(String[] args) throws CloneNotSupportedException {
        Date date = new Date();
        Video video = new Video("Kite Study Java", date);

        System.out.println(video);
        System.out.println(video.hashCode());


        // 浅克隆
        Video video_c = (Video) video.clone();

        System.out.println(video_c);
        System.out.println(video_c.hashCode());

        System.out.println("==========");

        date.setTime(220002020);
        System.out.println(video);
        System.out.println(video_c);

        System.out.println("==========");

        date = new Date();
        video.setCreateTime(date);

        System.out.println(video);
        System.out.println(video_c);
    }
}

输出结果

Video{name='Kite Study Java', createTime=Tue Apr 26 20:19:28 CST 2022}
1735600054
Video{name='Kite Study Java', createTime=Tue Apr 26 20:19:28 CST 2022}
21685669
==========
Video{name='Kite Study Java', createTime=Sat Jan 03 21:06:42 CST 1970}
Video{name='Kite Study Java', createTime=Tue Apr 26 20:19:28 CST 2022}
==========
Video{name='Kite Study Java', createTime=Tue Apr 26 20:19:28 CST 2022}
Video{name='Kite Study Java', createTime=Tue Apr 26 20:19:28 CST 2022}

适配器模式(结构型)

  • 对象适配器优点
    • 一个对象适配器可以把多个不同的适配者适配到同一个目标
    • 可以适配一个适配者的子类,由于适配器和适配者之间是关联关系,根据“里氏代换原则”,适配者的子类也可通过该适配器进行适配
  • 类适配器缺点
    • 对于Java、C#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者
    • 在Java、C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性
  • 适用场景
    • 系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码
    • 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作

案例一

// 要被适配的类
public class Adaptee {

    public void request() {
        System.out.println("连接网线上网");
    }
}
// 接口转换器的抽象实现
public interface NetToUSB {
    // 作用:处理请求
    public void handleRequest();
}
// 真正的适配器
public class Adapter extends Adaptee implements NetToUSB {

    @Override
    public void handleRequest() {
        super.request();
    }
}
public class Computer {
    public void net(NetToUSB adapter) {
        // 上网的具体实现
        adapter.handleRequest();
    }
    
    public static void main(String[] args) {
        // 电脑
        Computer computer = new Computer();
        // 网线
        // Adaptee adaptee = new Adaptee();
        // 转接器
        Adapter adapter = new Adapter();

        computer.net(adapter);
    }
}

Adaptee adaptee = new Adaptee(); 不需要显示出现,Adapter继承了Adaptee

案例二

// 要被适配的类
public class Adaptee {
    public void request() {
        System.out.println("连接网线上网");
    }
}
// 接口转换器的抽象实现
public interface NetToUSB {

    // 作用:处理请求
    public void handleRequest();

}
// 热拔插式
public class Adapter implements NetToUSB{
    private Adaptee adaptee;
    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }
    @Override
    public void handleRequest() {
        adaptee.request();
    }
}
public class Computer {

    public void net(NetToUSB adapter) {
        // 上网的具体实现
        adapter.handleRequest();
    }

    public static void main(String[] args) {
        // 电脑
        Computer computer = new Computer();
        // 网线
        Adaptee adaptee = new Adaptee();
        // 转接器
        Adapter adapter = new Adapter(adaptee);
        
        computer.net((NetToUSB) adapter);
    }
}

继承 (类适配器 单继承)

组合 (对象适配器 常用)

桥接模式(结构型)

image-20220426222732162

// 品牌
public interface Brand {void info();}

// Apple
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;
    public Computer(Brand brand) {
        this.brand = brand;
    }
    public void info() {
        // 自带品牌
        brand.info();
    }
}

// Laptop
public class Laptop extends Computer {

    public Laptop(Brand brand) {
        super(brand);
    }
    @Override
    public void info() {
        super.info();
        System.out.println("笔记本");
    }
}
// Desktop
public class Desktop extends Computer {
    public Desktop(Brand brand) {
        super(brand);
    }
    @Override
    public void info() {
        super.info();
        System.out.println("台式机");
    }
}
public class Test {
    public static void main(String[] args) {
        Computer computer = new Laptop(new Apple());
        computer.info();	// 苹果笔记本
        Computer computer2 = new Desktop(new Lenovo());
        computer2.info();	// 联想台式机
    }
}
  • 好处分析
    • 桥接模式偶尔类似于多继承方案,但是多继承方案违背了类的单一职责原则,复用性比较差,类的个数也非常多,桥接模式是比多继承方案更好的解决方法。极大的减少了子类的个数,从而降低管理和维护的成本
    • 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。符合开闭原则,就像一座桥,可以把两个变化的维度连接起来
  • 劣势分析
    • 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程
    • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性
  • 最佳实践
    • 如果一个系统需要在构建的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合
    • 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展
    • 虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用
  • 场景
    • Java语言通过Java虚拟机实现了平台的无关性
    • AWT中的Peer架构
    • JDBC驱动程序也是桥接模式的应用之一

代理模式(结构型)

静态代理

  • 抽象角色:一般会使用接口或者抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
  • 客户:访问代理对象的人!

代理模式的优点

  • 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
  • 公共业务就交给代理角色!实现了业务的分工!
  • 公共业务发生扩展的时候,方便集中管理!

缺点

  • 一个真实角色就会产生一个代理角色;代码量会翻倍-开发效率会变低

代码步骤

案例一

image-20220426213420608

  • 接口

    // 租房
    public interface Rent {
        public void rent();
    }
    
  • 真实角色

    // 房东
    public class Host implements Rent{
        @Override
        public void rent() {
            System.out.println("房东要出租房子了");
        }
    }
    
  • 代理角色

    // 代理
    public class Proxy implements Rent {
        private Host host;
    
        public Proxy() {
        }
        public Proxy(Host host) {
            this.host = host;
        }
        @Override
        public void rent() {
            host.rent();
        }
    
        // 看房
        public void seeHouse() {
            System.out.println("中介带你看房");
        }
    
        // 签合同
        public void signContract() {
            System.out.println("签租赁合同");
        }
    
        // 收中介费
        public void fare() {
            System.out.println("收中介费");
        }
    }
    
  • 客户端访问代理角色

    public class Client {
        public static void main(String[] args) {
            Host host = new Host();
    //        host.rent();
            // 代理
            Proxy proxy = new Proxy(host);
    
            proxy.rent();
            proxy.fare();
            proxy.seeHouse();
            proxy.signContract();
        }
    }
    

案例二

image-20220426213545932

接口

  • package com.lee.demo02;
    
    public interface UserService {
    
        // 增删改查
        public void add();
        public void delete();
        public void update();
        public void query();
    }
    
  • 真实角色

    public class UserServiceImpl implements UserService{
        @Override
        public void 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("查询一个用户");
        }
    }
    
  • 代理

    package com.lee.demo02;
    
    public class UserServiceProxy implements UserService{
    
        private UserServiceImpl userService;
    
        public void setUserService(UserServiceImpl userService) {
            this.userService = userService;
        }
    
        @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 void log(String msg) {
            System.out.println("使用了" + msg + "方法");
        }
    }
    
  • 客户端访问

    public class Client {
    
        public static void main(String[] args) {
    
            UserServiceImpl userService = new UserServiceImpl();
    
            UserServiceProxy proxy = new UserServiceProxy();
            proxy.setUserService(userService);
    
            proxy.add();
            proxy.delete();
            proxy.update();
            proxy.query();
        }
    }
    

动态代理

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的!
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
    • 基于接口---JDK动态代理【主要学习这个】
    • 基于类:cglib
    • java字节码实现:javasist

需要了解两个类:Proxy:代理,InvocationHandler:调用处理程序

优点

  • 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
  • 公共也就就交给代理角色!实现了业务的分工!
  • 公共业务发生扩展的时候,方便集中管理!
  • 一个动态代理类代理的是一个接口,一般就是对应的一类业务
  • 一个动态代理类可以代理多个类

案例一

  • 接口

    // 租房
    public interface Rent {
        public void rent();
    }
    
  • 真实角色

    // 房东
    public class Host implements Rent {
        @Override
        public void rent() {
            System.out.println("房东要出租房子了");
        }
    }
    
  • 动态代理

    package com.lee.demo03;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    // 使用这个类自动生成代理类
    public class ProxyInvocationHandler implements InvocationHandler {
    
        // 被代理的接口
        private Rent rent;
    
        public void setRent(Rent rent) {
            this.rent = rent;
        }
    
        // 生成得到代理类
        public Object getProxy() {
            return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
        }
    
        // 处理代理实例,并返回结果
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            seeHouse();
            fare();
            // 动态代理的本质就是使用反射机制实现的
            return method.invoke(rent, args);
        }
    
        // 看房
        public void seeHouse() {
            System.out.println("中介带你看房");
        }
    
        // 签合同
        public void signContract() {
            System.out.println("签租赁合同");
        }
    
        // 收中介费
        public void fare() {
            System.out.println("收中介费");
        }
    
    }
    
  • 客户端访问

    public class Client {
    
        public static void main(String[] args) {
    
            // 真实角色
            Host host = new Host();
    
            // 代理角色 : 现在没有
            ProxyInvocationHandler pih = new ProxyInvocationHandler();
            // 通过调用程序处理角色来处理要调用的接口对象
            pih.setRent(host);
    
            Rent proxy = (Rent) pih.getProxy();
            proxy.rent();
    
        }
    }
    

案例二

  • 接口

    public interface UserService {
    
        // 增删改查
        public void add();
        public void delete();
        public void update();
        public void query();
    
    }
    
  • 真实角色

    public class UserServiceImpl implements UserService {
        @Override
        public void 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("查询一个用户");
        }
    }
    
  • 动态代理

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    // 使用这个类自动生成代理类
    public class ProxyInvocationHandler implements InvocationHandler {
    
        // 被代理的接口
        private Object target;
    
        public void setTarget(Object target) {
            this.target = target;
        }
    
        // 生成得到代理类
        public Object getProxy() {
            return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        }
    
        // 处理代理实例,并返回结果
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            log(method.getName());
            // 动态代理的本质就是使用反射机制实现的
            return method.invoke(target, args);
        }
    
        // 代理抽象出来的功能
        public void log(String msg) {
            System.out.println("执行了" + msg + "方法");
    
        }
    
    }
    
  • 客户端访问

    public class Client {
    
        public static void main(String[] args) {
    
            // 真实角色
            UserServiceImpl userService = new UserServiceImpl();
            // UserServiceImplTWO 如果实现了,也可以代理
            // UserServiceImplTWO userService2 = new UserServiceImplTWO();
    
            // 代理角色, 不存在
            ProxyInvocationHandler pih = new ProxyInvocationHandler();
            // 设置要代理的对象
            pih.setTarget(userService);
    
            // 动态生成代理类
            UserService proxy = (UserService) pih.getProxy();
            proxy.add();
        }
    }
    

其他参见菜鸟教程设计模式

  • 创建型模式
    • 工厂模式
    • 抽象工厂模式
    • 单例模式
    • 建造者模式
    • 原型模式
  • 结构型模式
    • 适配器模式
    • 桥接模式
    • 过滤器模式
    • 组合模式
    • 装饰器模式
    • 外观模式
    • 享元模式
    • 代理模式
  • 行为型模式
    • 责任链模式
    • 命令模式
    • 解释器模式
    • 迭代器模式
    • 中介者模式
    • 备忘录模式
    • 观察者模式
    • 状态模式
    • 空对象模式
    • 策略模式
    • 模板模式
    • 访问者模式
  • J2EE 模式
    • MVC 模式
    • 业务代表模式
    • 组合实体模式
    • 数据访问对象模式
    • 前端控制器模式
    • 拦截过滤器模式
    • 服务定位器模式
    • 传输对象模式
    • 设计模式资源


这篇关于设计模式的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程