设计模式复习知识点(更新中)

第 1 章 设计模式的原则

  1. 单一职责原则:降低类的复杂度,一个类应该只有一个职责
  2. 里氏替换原则:子类在继承时尽量不要重写父类的方法,如果子类迫不得已要重写,就让子类和父类去继承更通俗的基类来降低二者的耦合性
  3. 依赖倒置原则:高层模块类不依赖低层模块类,两者都依赖其抽象接口
  4. 接口隔离原则:客户端只依赖它需要的接口,两个类间的依赖关系应该建立在最小的接口上
  5. 迪米特法则:如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用;如果一个类需要调用另一个类的某一个方法,可以通过第三者转发这个调用
  6. 开闭原则:一个类应当对提供方的扩展开放,对使用方的修改关闭。在设计一个模块的时候,应当在不必修改源代码的情况下改变这个模块的行为
  7. 组合 / 聚合复用原则:尽量使用组合和聚合少使用继承的关系来达到复用

如果公司规定司机入驻时必须有一辆自己的车才能去载客,此时 Driver 和 Car 是组合关系:

public class Driver {
    public Car car;
    public Driver() {
        car = new Car();        // 司机自己提供车
    }

    public void drive() {
        car.start();
    }
}

如果公司规定只要司机会开车,公司各种车任你挑去载客,此时 Driver 和 Car 是聚合关系:

public class Driver {
    public Car car;
    public void setCar(Car car){   // 由公司提供,什么车司机不清楚
        this.car = car;
    }

    public void drive() {
        car.start();
    }
}

第 2 章 UML 类图

2.1 具体类

具体类在类图中用矩形框表示,矩形框分为三层:第一层是类名字。第二层是类的成员变量;第三层是类的方法。成员变量以及方法前的访问修饰符用符号来表示:+ 表示 public,- 表示 private。

2.2 抽象类

抽象类用矩形框表示,抽象类的类名以及抽象方法的名字都用斜体字表示。

2.3 接口

接口在类图中也是用矩形框表示,但是与类的表示法不同的是,接口在类图中的第一层顶端用构造型 <> 表示,下面是接口的名字,第二层是方法。

2.4 六种关系

2.4.1 实现关系

实现关系是指接口及其实现类之间的关系:

2.4.2 泛化关系(继承关系)

泛化关系是指对象与对象之间的继承关系:

2.4.3 关联关系

关联关系是指一个对象含有另一个对象的引用:

2.4.4 依赖关系

如果对象 A 用到对象 B ,但是和 B 的关系不是太明显的时候,就可以把这种关系看作是依赖关系:

2.4.5 聚合关系

2.4.6 组合关系

第 3 章 单例模式

单例模式保证某个类只能存在一个对象实例,并且该类只提供一个获取对象实例的静态方法。

3.1 饿汉式实现
  • 优点:线程安全,没有加锁
  • 缺点:类加载时就加载了实例,如果没使用过该实例会浪费内存
class Singleton {
    // 1.私有化构造器
    private Singleton() {}

    // 2.类的内部创建静态化对象 instance
    private static Singleton instance = new Singleton();

    // 3.提供公共的静态方法 getInstance,返回当前类的对象 instance
    public static Singleton getInstance() {
        return instance;
    }
}
3.2 懒汉式实现(双重加锁 DCL)

实际开发中要保证线程安全,所以使用双重检查模式的懒汉式。

  • 优点:第一次调用对象才初始化,避免内存浪费
  • 缺点:必须加锁才能保证单例,但加锁会影响效率
class Singleton{
    // 1.私有化构造器
    private Singleton() {} 

    // 2.类的内部创建静态化对象 instance
    private static volatile Singleton instance;  

    // 3.提供公共的静态方法 getInstance,如果未创建对象则在方法内部创建
    public static Singleton getInstance(){
        if(instance == null){
            synchronized (Singleton.class) {
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}   
3.3 静态内部类实现

内部类只有在外部类被调用才加载,产生 INSTANCE 实例;又不用加锁,是最好的单例模式。使用内部类来实现单例模式如下:

public class Singleton {
    private Singleton(){}

    private static class Inner {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance(){
        return Inner.INSTANCE;
    }
}
3.4 枚举类实现

不仅避免多线程同步问题,而且还能防止反序列化重新创建对象(JVM 保证枚举类型不能被反射并且构造函数只被执行一次):

public enum Singleton {
     INSTANCE;
     public void getInstance() {
          System.out.println("我是一个单例!");
     }
}

第 4 章 抽象工厂模式

抽象工厂模式同工厂方法模式一样,也是由抽象工厂、具体工厂、抽象产品和具体产品等 4 个要素构成。

  1. 抽象工厂:提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品
  2. 具体工厂:主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建
  3. 抽象产品:定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品
  4. 具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系

我们使用抽象工厂模式模仿生产小米手机、华为手机、小米路由器、华为路由器:

4.1 抽象工厂

抽象工厂中包含创建手机和创建路由器的两个方法:

public interface ProductFactory {
    Phone getPhone();
    Router getRouter();
}
4.2 具体工厂

实现抽象工厂中的两个抽象方法,完成小米手机、小米路由器、华为手机、华为路由器的产品创建。

小米工厂:

public class XiaomiFactory implements ProductFactory {

    @Override
    public Phone getPhone() {
        return new XiaomiPhone();
    }

    @Override
    public Router getRouter() {
        return new XiaomiRouter();
    }
}

华为工厂:

public class HuaweiFactory implements ProductFactory {
    @Override
    public Phone getPhone() {
        return new HuaweiPhone();
    }

    @Override
    public Router getRouter() {
        return new HuaweiRouter();
    }
}
4.3 抽象产品

定义了产品的规范,描述了产品的主要特性和功能。

手机接口定义了拍照片和发短信两个功能:

public interface Phone {
    void photograph();
    void sendMessage();
}

路由器接口定义了开 wifi 和关 wifi 两个功能:

public interface Router {
    void openWifi();
    void closeWifi();
}
4.4 具体产品

实现抽象产品角色所定义的接口。

小米手机:

public class XiaomiPhone implements Phone {
    @Override
    public void photograph() {
        System.out.println("小米手机拍照片");
    }

    @Override
    public void sendMessage() {
        System.out.println("小米手机发短信");
    }
}
小米路由器:

    public class XiaomiRouter implements Router {
        @Override
        public void openWifi() {
            System.out.println("小米路由器打开WiFi");
        }
     
        @Override
        public void closeWifi() {
            System.out.println("小米路由器关闭WiFi");
        }
    }

华为手机和华为路由器同理。

第 5 章 适配器模式

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。适配器模式将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

比如美国电器电压是 110V,中国是 220V,就要有一个适配器将 110V 转化为 220V。

5.1 需要被适配的类
public class Voltage110V {
    public int output110V(){
        return 110;
    }
}
5.2 适配器接口
public interface Voltage220V {
    int output220V();
}
5.3 适配后的类
public class VoltageAdapter implements Voltage220V {
    private Voltage110V voltage110V;

    public VoltageAdapter(Voltage110V voltage110V) {
        this.voltage110V = voltage110V;
    }

    @Override
    public int output220V() {
        int dstV = 0;
        if (voltage110V != null) {
            int srcV = voltage110V.output110V();
            dstV = srcV * 2;
        }
        return dstV;
    }
}
5.4 调用
public class Phone {
    public void charging(Voltage220V voltage220V){
        if(voltage220V.output220V() == 220){
            System.out.println("电压是220V,可以使用");
        }else {
            System.out.println("电压不是220V,不可以使用");
        }
    }
}
public class Client {
    public static void main(String[] args) {
        Phone phone = new Phone();
        phone.charging(new VoltageAdapter(new Voltage110V()));
    }
}

第 6 章 代理模式

动态代理在内存中构建代理对象。代理类所在包 java.lang.reflect.proxy,JDK 代理使用 newProxyInstance 方法,接收三个参数 ClassLoader loader,Class<?>[] interfaces,InvocationHandler h

6.1 JDK 代理

目标对象需要实现接口,使用 JDK 代理。

6.1.1 需要代理的接口和其实现类
public interface Calculator {  
    int add(int i, int j);
    int sub(int i, int j);  
}
public class CalculatorLogImpl implements Calculator { 
    @Override
    public int add(int i, int j) {
        int result = i + j;
        return result;
    }

    @Override
    public int sub(int i, int j) {
        int result = i - j;
        return result;
    }
}
6.1.2 生产代理对象的工厂类 ProxyFactory
public class ProxyFactory {
    private Object target;   
    public ProxyFactory(Object target) {
        this.target = target;              // 依赖目标对象
    }

    // getProxy() 方法返回代理对象 Proxy.newProxyInstance()
    public Object getProxy() {
        // 参数 1:加载动态生成的代理类的类加载器
        ClassLoader classLoader = target.getClass().getClassLoader();
        // 参数 2:目标对象实现的所有接口的 class 类型数组
        Class<?>[] interfaces = target.getClass().getInterfaces();
        // 参数 3:设置代理对象实现目标对象方法的过程
        InvocationHandler invocationHandler = new InvocationHandler() {
            // invoke() 代理类中重写接口中的抽象方法
            /**
             * proxy:代理对象
             * method:代理对象需要实现的方法,即需要重写的方法
             * args:method所对应方法的参数
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object result = null;
                try {
                    // 方法调用前输出日志
                    System.out.println("[日志] " + method.getName() + ",参数:" + Arrays.toString(args));
                    // 调用目标方法
                    result = method.invoke(target, args);
                    // 方法调用后输出日志
                    System.out.println("[日志] " + method.getName() + ",结果:" + result);
                } catch (Exception e) {
                    e.printStackTrace();
                    System.out.println("[日志] " + method.getName() + ",异常:" + e.getMessage());
                } finally {
                    System.out.println("[日志] " + method.getName() + ",方法执行完毕");
                }
                return result;
            }
        };
        return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
    }
}
6.1.3 测试
public class CalculatorTest {
    public static void main(String[] args) {
        // 创建代理对象(动态):同一个代理对象可以代理多个被代理对象
        ProxyFactory proxyFactory = new ProxyFactory(new CalculatorImpl());
        // 返回代理对象
        Calculator calculator = (Calculator) proxyFactory.getProxy();
        calculator.add(1,2);
    }
}
6.2 CGLib 代理

目标对象不需要实现接口,使用 CGLib 代理。底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类。

6.2.1 引入 CGLib 的依赖
 <dependencies>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
</dependencies>   
6.2.2 目标对象
    public class CalculatorDao {
        public void calculate(){
            System.out.println("计算,使用CGLib代理");
        }
    }
6.2.3 生产代理对象的工厂类 ProxyFactory
public class ProxyFactory implements MethodInterceptor {
    private Object target;   
    public ProxyFactory(Object target) {
        this.target = target;                  // 依赖目标对象
    }

    public Object getProxyInstance(){
        Enhancer enhancer = new Enhancer();    // 创建工具类
        enhancer.setSuperclass(User.class);    // 设置 enhancer 的父类
        enhancer.setCallback(new MyProxy());   // 设置 enhancer 的回调对象
        return enhancer.create();
    }

    /**
     * @param o           cglib 生成的代理对象
     * @param method      被代理对象的方法
     * @param objects     传入方法的参数
     * @param methodProxy 代理的方法
     */
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("CGLib代理开始");
        Object returnVal = method.invoke(target, objects);
        System.out.println("CGLib代理结束");
        return returnVal ;
    }
}
6.2.4 测试
public class TestCalculator {
    public static void main(String[] args) {
        // 创建代理对象(动态):同一个代理对象可以代理多个被代理对象
        ProxyFactory proxyFactory = new ProxyFactory(new CalculatorDao());
        // 返回代理对象
        CalculatorDao proxyInstance = (CalculatorDao) proxyFactory.getProxy();
        proxyInstance.calculate();
    }
}

第 7 章 模板模式

用户看直播是有一套固定的模板流程的:登录—进入房间—获取音视频流—观看—停止音视频流—退出房间 虽然两套 SDK 每一个步骤的实现方式不同,但是基本都是遵循同一套流程。这时候就可以用到模板模式。

7.1 抽象类

先定义一个模板类 LivePlay,其中 seeLivePlay() 就是所谓的模板方法,为了不被子类重写,它被设置为 final 的,其定义了一个算法骨架:

public abstract class LivePlay {
    // 模板方法
    public final void seeLivePlay() {
        login();
        openRoom();
        startAudioAndVideoStream();
        pushVideoStream();
        stopAudioAndVideoStream();
        closeRoom();
    }

    private void login() {
        System.out.println("用户登录");
    }

    public abstract void openRoom();                    // 打开房间
    public abstract void startAudioAndVideoStream();    // 打开音视频流
    public abstract void stopAudioAndVideoStream();     // 关闭音视频流
    public abstract void closeRoom();                   // 关闭房间

    /* 父类默认不做任何事的方法称为钩子方法,方便子类重写 */
    // 旁路推流
    public void pushVideoStream() {
    }                   
}
7.2 具体实现类

虎牙直播类:

public class HUYALivePlay extends LivePlay {
    @Override
    public void openRoom() {System.out.println("虎牙打开房间");}

    @Override
    public void startAudioAndVideoStream() {System.out.println("虎牙打开音视频流");}

    @Override
    public void stopAudioAndVideoStream() {System.out.println("虎牙关闭音视频流");}

    @Override
    public void closeRoom() {System.out.println("虎牙关闭房间");}

    // 重写钩子方法,提供旁路推流功能
    @Override
    public void pushVideoStream() {
        super.pushVideoStream();
        System.out.println("虎牙进行旁路推流");
    }
}

假设斗鱼直播类 DOUYULivePlay 没有旁路推流功能,就不用重写相应的钩子方法:

public class DOUYULivePlay extends LivePlay {
    @Override
    public void openRoom() {System.out.println("斗鱼打开房间");}

    @Override
    public void startAudioAndVideoStream() {System.out.println("斗鱼打开音视频流");}

    @Override
    public void stopAudioAndVideoStream() {System.out.println("斗鱼关闭音视频流");}

    @Override
    public void closeRoom() {System.out.println("斗鱼关闭房间");}
}
7.3 客户端调用
public class Client{ 
     public static void main(String[] args) {
            LivePlay hyLive = new HUYALivePlay();
            hyLive.seeLivePlay();

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

            LivePlay dyLive = new DOUYULivePlay();
            dyLive.seeLivePlay();
     }
}

第 8 章 策略模式

当写代码的时候发现一个操作有好多种实现方法,而需要根据不同的情况使用 if-else 等分支结构来确定使用哪种实现方式的时候,就使用策略模式。

举例,目前有三种出行方式的策略可以到达同一个目的地,使用何种策略取决于调用者(客户端)。

8.1 策略接口
    public interface TravelStrategy {
        int travelCosts(int distance);
    }
8.2 封装各个算法

乘坐公交车算法:

public class ByBus implements TravelStrategy {
    @Override
    public int travelCosts(int distance) {
        return distance < 10 ? 4 : 6;
    }
}

乘坐滴滴算法:

public class ByDiDi implements TravelStrategy {
    @Override
    public int travelCosts(int distance) {
        return distance < 3 ? 8 : (8 + (distance - 3) * 3);
    }
}

骑共享单车算法:

public class BySharedBicycle implements TravelStrategy {
    @Override
    public int travelCosts(int distance) {
        return 2;
    }
}
8.3 使用算法
public class TravelCostCalculator {
    public int goToDestination(TravelStrategy strategy, int distance){
        return strategy.travelCosts(distance);
    }
}
8.4 客户端调用
public class Client {
    public static void main(String[] args) {
        TravelCostCalculator calculator = new TravelCostCalculator();
        System.out.println(String.format("乘坐公交车到目的地的花费为:%d块人民币",
                calculator.goToDestination(new ByBus(), 10)));
        System.out.println(String.format("乘坐滴滴到目的地的花费为:%d块人民币",
                calculator.goToDestination(new ByDiDi(), 10)));
        System.out.println(String.format("骑共享单车到目的地的花费为:%d块人民币",
                calculator.goToDestination(new BySharedBicycle(), 10)));
    }
}

第 9 章 建造者模式

当一个类的构造函数参数个数超过 4 个,而且这些参数有些是可选的参数,考虑使用建造者模式。建造者模式有以下四个组成:

  1. Product:最终要生成的具体产品对象,例如 Computer 实例。
  2. Builder:建造者的接口,定义了构建 Product 的抽象步骤,其中包含一个用来返回最终产品的方法 Product getProduct()
  3. ConcreteBuilder: Builder 接口的具体实现类
  4. Director: 指导者类,决定如何建造最终产品的算法。其会包含一个负责组装的方法 void Construct(Builder builder), 在这个方法中通过调用 builder 的方法,就可以设置 builder,等设置完成后,就可以通过 builder 的 getProduct() 方法获得最终的产品
9.1 目标产品 Product

目标产品的基类是 Computer:

public class Computer {
    private String cpu;        // 必要参数
    private String ram;        // 必要参数
    private int usbCount;      // 可选参数
    private String keyboard;   // 可选参数
    private String display;    // 可选参数

    public Computer(String cpu, String ram) {
        this.cpu = cpu;
        this.ram = ram;
    }
    public void setUsbCount(int usbCount) {this.usbCount = usbCount;}
    public void setKeyboard(String keyboard) {this.keyboard = keyboard;}
    public void setDisplay(String display) {this.display = display;}
    @Override
    public String toString() {
        return "Computer{" +
                "cpu='" + cpu + ''' +
                ", ram='" + ram + ''' +
                ", usbCount=" + usbCount +
                ", keyboard='" + keyboard + ''' +
                ", display='" + display + ''' +
                '}';
    }
}
9.2 接口

建造 Computer 的抽象接口:

interface ComputerBuilder {
    void setUsbCount();    
    void setKeyboard();
    void setDisplay();

    Computer getComputer();
}
9.3 接口的具体实现类

苹果电脑实现类:

public class MacComputerBuilder implements ComputerBuilder {
    private Computer computer;
    public MacComputerBuilder(String cpu, String ram) {
        computer = new Computer(cpu, ram);
    }
    @Override
    public void setUsbCount() {
        computer.setUsbCount(2);
    }
    @Override
    public void setKeyboard() {
        computer.setKeyboard("苹果键盘");
    }
    @Override
    public void setDisplay() {
        computer.setDisplay("苹果显示器");
    }
    @Override
    public Computer getComputer() {
        return computer;
    }
}

联想电脑实现类:

public class LenovoComputerBuilder implements ComputerBuilder {
    private Computer computer;
    public LenovoComputerBuilder(String cpu, String ram) {
        computer=new Computer(cpu,ram);
    }
    @Override
    public void setUsbCount() {
        computer.setUsbCount(3);
    }
    @Override
    public void setKeyboard() {
        computer.setKeyboard("联想键盘");
    }
    @Override
    public void setDisplay() {
        computer.setDisplay("联想显示器");
    }
    @Override
    public Computer getComputer() {
        return computer;
    }
}
9.4 指导者类
public class ComputerDirector {
    public void makeComputer(ComputerBuilder builder){
        builder.setUsbCount();
        builder.setDisplay();
        builder.setKeyboard();
    }
}
9.5 测试
public class Main {
    public static void main(String[] args) {
        ComputerDirector director = new ComputerDirector();

        ComputerBuilder builder = new MacComputerBuilder("I5处理器", "三星125");
        director.makeComputer(builder);
        Computer macComputer = builder.getComputer();
        System.out.println("mac computer:" + macComputer.toString());

        ComputerBuilder lenovoBuilder = new LenovoComputerBuilder("I7处理器", "海力士222");
        director.makeComputer(lenovoBuilder);
        Computer lenovoComputer = lenovoBuilder.getComputer();
        System.out.println("lenovo computer:" + lenovoComputer.toString());
    }
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值