zoukankan      html  css  js  c++  java
  • 一文总结设计模式

    前言

    • 看了很多寓教于学写设计模式的,看的有点头疼,注意力全都在故事上了,满脑子都是鸭子,餐厅之类,还有一堆和设计模式不相关的话,翻书都翻的挺累的。
    • 这里我整理了下23种设计模式,没什么多余的话,代码演示,简单粗暴,借鉴的地方都附上了参考链接(做个优秀的搬运工),没附上的是自己总结的。
    • 借鉴的例子代码,基本都做了一些精简,如果相关例子写的有什么不准确,麻烦在评论里面指出来,最好附上代码,我会尽快修改文章中的相关实例
    • 23种设计模式,一文呈现,方便大家和自己查询,也方便自己随时修改,请配合文章旁边的大纲食用。

    总述

    7种面向对象设计原则

    设计原则名称 定 义
    单一职责原则(Single Responsibility Principle, SRP) 一个类只负责一个功能领域中的相应职责
    开闭原则(Open-Closed Principle, OCP) 软件实体应对扩展开放,而对修改关闭
    里氏代换原则(Liskov Substitution Principle, LSP) 所有引用基类对象的地方能够透明地使用其子类的对象
    迪米特法则(Law of Demeter, LoD) 一个软件实体应当尽可能少地与其他实体发生相互作用
    接口隔离原则(Interface Segregation Principle, ISP) 使用多个专门的接口,而不使用单一的总接口
    依赖倒转原则(Dependence Inversion Principle, DIP) 抽象不应该依赖于细节,细节应该依赖于抽象
    合成复用原则(Composite Reuse Principle, CRP) 尽量使用对象组合,而不是继承来达到复用的目的
    • 设计模式可分为创建型(Creational),结构型(Structural)和行为型(Behavioral)三种

      • 创建型模式主要用于描述如何创建对象(5种)
      • 结构型模式主要用于描述如何实现类或对象的组合(7种)
      • 行为型模式主要用于描述类或对象怎样交互以及怎样分配职责(11种)
    • 图解:花了我蛮多精力画的,有需要到的,可以参照参照

      • 图片是高清图,下载后查看,体验更好

    设计模式

    原则简述

    1. 单一职责原则

    定义:一个类只有一个引起它变化的原因。

    理解:对功能进行分类,代码进行解耦,一个类只管一件事

    举例:就比如一个网络请求框架大体分为:请求类,缓存类,配置类,不能把这3个混在一起,必须分为3个类去实现不同的功能。

    2.开闭原则

    定义:一个实体(类、函数、模块等)应该对外扩展开放,对内修改关闭

    理解:每次发生变化时,要通过新增代码来增强现有类型的行为,而不是修改原有代码。

    举例:就比如在软件的生命周期内,因为产品迭代,软件升级维护等原因,需要对原有代码进行修改时,可能会给原有代码引入错误,也可能使得我们对整个功能不得不进行重构,并且需要对原有代码进行重新测试,这样的话,对开发周期影响很大,所以开闭原则刚好解决这个问题。

    3. 里氏替换原则

    定义:继承必须确保父类所拥有的性质在子类中仍然成立。

    理解:在继承类时,除了扩展一些新的功能之外,尽量不要删除或者修改对父类方法的引用,也尽量不要重写父类的方法。

    举例:你看啊,就比如Object有个方法,叫equals,如果不遵守里氏代替原则,它的子类重写了equals这个方法,并且返回了个null,这个子类的下一个继承者也会返回null,那么,在不同开发人员开发时,可能考虑不到这个问题,那么就可能导致程序崩溃。

    4.迪米特法则

    定义:一个模块或对象应尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立,这样当一个模块修改时,影响的模块就会越少,扩展起来更加容易。

    理解:一个对象应该对其他对象有最少的了解;一个类应该对自己需要耦合或调用的类知道得最少,类的内部如何实现、如何复杂都与调用者或者依赖者没关系,调用者或者依赖者只需要知道他需要的方法即可,其他的一概不关心。类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。

    举例:一般在使用框架的时候,框架的开发者会抽出一个类供外部调用,而这个主要的类像是一个中介一样去调用框架里面的其他类,恰恰框架里面其他类一般都是不可访问(调用)的,这个框架就遵守了迪米特原则,其他开发人员只关心调用的方法,并不需要关心功能具体如何实现。

    5.接口隔离原则

    定义:使用多个专门功能的接口,而不是使用单一的总接口

    理解:在定义接口方法时应该合理化,尽量追求简单最小,避免接口臃肿

    举例:在实际开发中,往往为了节省时间,可能会将多个功能的方法抽成一个接口,其实这设计理念不正确的,这样会使接口处于臃肿的状态,这时就需要合理的拆分接口中的方法,另外抽取成一个独立的接口,避免原有的接口臃肿导致代码理解困难。

    6.依赖倒置原则

    定义:细节应该依赖于抽象,而抽象不应该依赖于细节

    理解:高层模块不依赖低层次模块的细节,不依赖具体的类,而是依赖于接口

    举例:比如说我们写一个网络框架,为了满足不同开发者的需求,即能使用高效的OkHttp框架,也可以使用原生的API。那么是如何进行切换的呢,这个时候需要面向接口编程思想了,把一些网络请求的方法封装成一个接口,然后分别创建OkHttp和原生API的接口实现类,当然也可以扩展其他网络框架的应用。

    7.合成复用原则

    定义:尽量使用对象组合,而不是继承来达到复用的目的。

    理解:它要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

    举例:相当于我们开发软件,一个模块的构造,就像搭积木一样,通过组合的形式,完成整体的构建;现在声明式UI框架,对于这种思想比较贯彻。

    参考

    创建型模式

    单例模式

    饿汉模式

    public class Singleton {
        // 只会实例化一次
        private static Singleton instance = new Singleton();       
     
        // 私有构造方法,防止被实例化
        private Singleton() {}
     
        public static Singleton getInstance() {
            return instance;
        }
    }
    
    • 在未使用该类的时候,也会实例化对象,会造成资源浪费,除非在一定会用到该种类的场景,否则不建议使用。

    懒汉模式

    public class Singleton {
        // 持有私有静态实例,防止被引用;赋值为null,目的是实现延迟加载;volatile修饰是禁止重排
        private volatile static Singleton instance = null;
     
        // 私有构造方法,防止被实例化
        private Singleton() {}
     
        public static Singleton getInstance() {
            if (instance == null) {
                synchronized (instance) {
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
    
    • 使用volatile修饰,可禁止重排,在多线程场景也能保证正常运行;只会在使用的时候,才会去实例化对象,一般场景都可以使用。

    静态内部类模式

    public class Singleton {
        // 私有构造方法,防止被实例化
        private Singleton() {}
     
        // 获取实例
        public static Singleton getInstance() {
            return Inner.instance;
        }
     
        // 使用一个内部类来维护单例,只有在该类被加载的时候,才会实例化对象
        private static class Inner {
            private static Singleton instance = new Singleton();
        }
    }
    
    • 饿汉模式升级版,解决了资源浪费问题,同时也能保证线程安全;只有在内部类被加载的时候,才会去实例化对象;相对于懒汉模式,静态内部类的方式避免了排队进同步代码块做null的判断,性能优于懒汉模式,推荐使用。

    枚举模式

    public enum Singleton {
        // 创建一个枚举对象,该对象天生为单例
        INSTANCE;
        public Singleton getInstance(){
            return INSTANCE;
        }
    }
    
    • 推荐:effective java中最佳的单例实现模式就是枚举模式,JVM来帮我们保证线程安全和单一实例,在反射和序列化的场景中,仍能保证单一实例。

    参考

    工厂模式

    简单工厂模式

    • 以手机为例来说明
    • 定义一个接口,开机的时候,手机界面上,会显示该手机系统
    public interface Phone {
        //实施开机操作的时候,返回手机系统
        String startUp();
    }
    
    • 实现一个Android手机类,实现Phone接口,返回“Android”手机系统
    public class AndroidPhone implements Phone {
        @Override
        public String startUp() {
            return "Android";
        }
    }
    
    • 实现一个IOS手机类
    public class IOSPhone implements Phone {
        @Override
        public String startUp() {
            return "IOS";
        }
    }
    
    • 创建手机工厂管理类
    public class PhoneFactory {
        private static Phone mPhone;
    
        //根据系统关键字获取相应手机对象
        public static Phone createPhone(String system) {
            switch (system) {
                case "Android":
                    mPhone = new AndroidPhone();
                    break;
                case "IOS":
                    mPhone = new IOSPhone();
                    break;
            }
            return mPhone;
        }
    }
    
    • 使用
    public void test() {
        Phone android = PhoneFactory.createPhone("Android");
        Phone ios = PhoneFactory.createPhone("IOS");
    
        System.out.print(android.startUp() + "\n");
        System.out.print(ios.startUp() + "\n");
    }
    
    • 结果显示:Android IOS

    工厂模式

    • 工厂模式区别与简单工厂模式的是,工厂模式不是用一个统一的工厂类来管理所有对象,而是每一个对象都有不同的工厂相对应。
    • 继续以手机为例说明,需要针对Android和IOS手机这俩个实现类,去构建不同的工厂,这边用接口去规范不同的工厂类
    • 手机工厂接口
    public interface PhoneFactory {
        //获取相关手机实例
        Phone getPhone();
    }
    
    • 创建Android手机工厂
    public class AndroidPhoneFactory implements PhoneFactory {
        @Override
        public Phone getPhone() {
            return new AndroidPhone();
        }
    }
    
    • 创建IOS手机工厂
    public class IOSPhoneFactory implements PhoneFactory {
        @Override
        public Phone getPhone() {
            return new IOSPhone();
        }
    }
    
    • 使用
    public void test() {
        PhoneFactory androidMiFactory = new AndroidPhoneFactory();
        PhoneFactory iosFactory = new IOSPhoneFactory();
    
        System.out.print(androidFactory.getPhone().startUp() + "\n");
        System.out.print(iosFactory.getPhone().startUp() + "\n");
    }
    
    • 结果显示:Android IOS

    抽象工厂模式

    • 抽象模式是对工厂模式的进一步优化,工厂类不单单只能创建一个对象,而是能创建一组对象。
    • 在这里,大家可能存在一个疑惑,简单工厂不也是创建一组对象吗?是的,在这点上,俩者是非常非常相似的,区别在于:简单工厂模式是外部传值进去,以获取不同对象;抽象工厂模式直接通过方法获取对象,不需要传值。
    • 继续以手机为例说明,咱们用俩种方式来看看,中间工厂类来创建一组对象

    1、第一种方式

    • 定义一个接口,这个接口里面是获取一系列的手机系统的实例
    public interface PhoneSystem {
        //获取Android手机实例
        Phone getAndroid();
        
        //获取IOS手机实例
        Phone getIOS();
    }
    
    • 实现抽象工厂的这个接口
    public class PhoneFactory implements PhoneSystem {
        @Override
        public Phone getAndroid() {
            return new AndroidPhone();
        }
    
        @Override
        public Phone getIOS() {
            return new IOSPhone();
        }
    }
    
    • 使用
    public void test() {
        PhoneSystem phoneSystem = new PhoneFactory();
    
        Phone android = phoneSystem.getXiaoMi();
        Phone ios = phoneSystem.getHuaWei();
    
        System.out.print(android.startUp() + "\n");
        System.out.print(ios.startUp() + "\n");
    }
    
    • 结果显示:Android IOS

    2、第二种方式

    • 在这里思考下,抽象工厂模式,是在工厂类里面创建一组对象,与外层交互,不是通过关键字去返回相应对象,而是通过某个共性方法去返回“符合条件的实例”。
    • 这里假设一个场景:我们定义的手机对象,其中的开机功能,只在对应的手机上才会起作用(Android手机想开机,只能使用Android手机类中的开机方法,IOS也是如此),在这里,假设此款手机是Android手机。
    • 此处,我们不定义接口,直接创建抽象工厂类。
    public class PhoneFactory {
        private static Phone instance;
        //模拟个数据
        private String SYSTEM = "IOS";
    
        public static Phone getInstance() {
            if("Android".equals(SYSTEM))
            {
                return new AndroidPhone();
            }
            if("IOS".equals(SYSTEM))
            {
                return new IOSPhone();
            }
            return null;
        }
    }
    
    • 使用
    public static void test() {
        Phone phone = PhoneFactory.getInstance();
        if (phone == null) 
            return;
        System.out.print(phone.startUp() + "\n");
    }
    
    • 结果显示:IOS

    建造者模式

    说明

    1. 在Computer 中创建一个静态内部类 Builder,然后将Computer 中的参数都复制到Builder类中。
    2. 在Computer中创建一个private的构造函数,参数为Builder类型
    3. 在Builder中创建一个public的构造函数,参数为Computer中必填的那些参数,cpu 和ram。
    4. 在Builder中创建设置函数,对Computer中那些可选参数进行赋值,返回值为Builder类型的实例
    5. 在Builder中创建一个build()方法,在其中构建Computer的实例并返回

    实现

    public class Computer {
        private final String cpu;//必须
        private final String ram;//必须
        private final int usbCount;//可选
        private final String keyboard;//可选
        private final String display;//可选
    
        private Computer(Builder builder){
            this.cpu=builder.cpu;
            this.ram=builder.ram;
            this.usbCount=builder.usbCount;
            this.keyboard=builder.keyboard;
            this.display=builder.display;
        }
        
        public static class Builder{
            private String cpu;//必须
            private String ram;//必须
            private int usbCount;//可选
            private String keyboard;//可选
            private String display;//可选
    
            public Builder(String cup,String ram){
                this.cpu=cup;
                this.ram=ram;
            }
    
            public Builder setUsbCount(int usbCount) {
                this.usbCount = usbCount;
                return this;
            }
            public Builder setKeyboard(String keyboard) {
                this.keyboard = keyboard;
                return this;
            }
            public Builder setDisplay(String display) {
                this.display = display;
                return this;
            }        
            public Computer build(){
                return new Computer(this);
            }
        }
    }
    

    使用

    Computer computer = new Computer.Builder("因特尔","三星")
        .setDisplay("三星24寸")
        .setKeyboard("罗技")
        .setUsbCount(2)
        .build();
    

    参考

    原型模式

    定义

    • 原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。

    实现

    public class Prototype implements Cloneable, Serializable {
        private static final long serialVersionUID = 1L;
        private String string;
        private SerializableObject obj;
     
        // 浅复制
        public Object clone() throws CloneNotSupportedException {
            Prototype proto = (Prototype) super.clone();
            return proto;
        }
     
        // 深复制
        public Object deepClone() throws IOException, ClassNotFoundException {
            // 写入当前对象的二进制流
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
     
            // 读出二进制流产生的新对象 
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            return ois.readObject();
        }
     
        public String getString() {
            return string;
        }
     
        public void setString(String string) {
            this.string = string;
        }
     
        public SerializableObject getObj() {
            return obj;
        }
     
        public void setObj(SerializableObject obj) {
            this.obj = obj;
        }
    }
     
    class SerializableObject implements Serializable {
        private static final long serialVersionUID = 1L;
    }
    

    参考

    结构型模式

    适配器模式

    类适配

    • 有一个已存在的将被适配的类
    public class Adaptee {
        public void adapteeRequest() {
            System.out.println("被适配者的方法");
        }
    }
    
    • 定义一个目标接口
    public interface Target {
        void request();
    }
    
    • 怎么才可以在目标接口中的 request() 调用 Adaptee 的 adapteeRequest() 方法呢?
      • 通过一个适配器类,实现 Target 接口,同时继承了 Adaptee 类,然后在实现的 request() 方法中调用父类的 adapteeRequest() 即可实现
    public class Adapter extends Adaptee implements Target{
        @Override
        public void request() {
            System.out.println("concreteTarget目标方法");
            super.adapteeRequest();
        }
    }
    
    • 测试一下
    public class Test {
        public static void main(String[] args) {
            Target adapterTarget = new Adapter();
            adapterTarget.request();
        }
    }
    
    • 输出
    concreteTarget目标方法
    被适配者的方法
    

    对象适配

    • 电源适配器:定义输出交流电接口,输出220V交流电类
    public interface AC {
        int outputAC();
    }
    
    public class AC220 implements AC {
        public final int output = 220;
    
        @Override
        public int outputAC() {
            return output;
        }
    }
    
    • 适配器接口,outputDC5V() 方法则用于将输入的电压变换为 5V 后输出
    public interface DC5Adapter {
        int outputDC5V(AC ac);
    }
    
    • 实现电源适配器
    public class PowerAdapter implements DC5Adapter {
        public static final int voltage = 220; 
        
        @Override
        public int outputDC5V(AC ac) {
            int adapterInput = ac.outputAC();
            //变压器...
            int adapterOutput = adapterInput / 44;
            System.out.println("使用PowerAdapter变压适配器,输入AC:" + adapterInput + "V" 
                               + ",输出DC:" + adapterOutput + "V");
            return adapterOutput;
        }
    }
    
    • 使用
    public class Test {
        private List<DC5Adapter> adapters = new LinkedList<DC5Adapter>();
    
        public static void main(String[] args) {
            AC ac = new AC220(); //实例化220v对象
            DC5Adapter adapter = new PowerAdapter(); //实例化适配器
            adapter.outputDC5V(ac); 
        }
    }
    
    • 输出
    使用PowerAdapter变压适配器,输入AC:220V,输出DC:5V
    

    接口适配

    • 在实际开发中,经常会遇到接口中定义了太多的方法,以致于有时我们在一些实现类中并不是都需要
    public interface Sourceable {
        public void method1();
        public void method2();
    }
    
    • 抽象类Wrapper:
    public abstract class Wrapper implements Sourceable{
        public void method1(){}
        public void method2(){}
    }
    
    • 可选择的去实现,我们想要的方法
    public class SourceSub extends Wrapper {
        @Override
        public void method1(){
            System.out.println("实现方法一");
        }
    }
    

    参考

    装饰模式

    定义

    • 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

    实现

    • 创建一个接口
    public interface Shape {
        void draw();
    }
    
    • 创建俩个实现类
    public class Rectangle implements Shape {
        @Override
        public void draw() {
            System.out.println("Shape: Rectangle");
        }
    }
    public class Circle implements Shape {
        @Override
        public void draw() {
            System.out.println("Shape: Circle");
        }
    }
    
    • 创建实现了 Shape 接口的抽象装饰类
    public abstract class ShapeDecorator implements Shape {
        protected Shape decoratedShape;
    
        public ShapeDecorator(Shape decoratedShape){
            this.decoratedShape = decoratedShape;
        }
    
        @Override
        public void draw(){
            decoratedShape.draw();
            System.out.println("Border Color: Red");
        }  
    }
    
    • 测试
    public class Test {
        public static void main(String[] args) {
            Shape circle = new Circle();
            ShapeDecorator circleShape = new ShapeDecorator(new Circle());
            ShapeDecorator rectangleShape = new ShapeDecorator(new Rectangle());
            System.out.println("Circle with normal border");
            circle.draw();
    
            System.out.println("\nCircle of red border");
            circleShape.draw();
    
            System.out.println("\nRectangle of red border");
            rectangleShape.draw();
        }
    }
    
    • 结果
    Circle with normal border
    Shape: Circle
    
    Circle of red border
    Shape: Circle
    Border Color: Red
    
    Rectangle of red border
    Shape: Rectangle
    Border Color: Red
    

    参考

    代理模式

    定义

    • 在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式;在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。
    • 其实每个模式名称就表明了该模式的作用,代理模式就是多一个代理类出来,替原对象进行一些操作,比如我们在租房子的时候回去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做,此处的代理就是这个意思。再如我们有的时候打官司,我们需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法。

    实现

    • 创建一个接口
    public interface Shape {
        void draw();
    }
    
    • 实现
    public class Circle implements Shape {
        @Override
        public void draw() {
            System.out.println("Shape: Circle");
        }
    }
    
    • 创建一个代理类
    public class ProxyShape implements Shape {
         private Circle circle;
        
        @Override
        public void draw() {
            if(circle == null){
                circle = new Circle();
            }
            circle.draw();
        }
    }
    
    • 测试
    public class Test {
        public static void main(String[] args) {
            Shape shapeProxy = new ProxyShape();
            shape.draw();  
        }
    }
    
    • 结果
    Shape: Circle
    

    参考

    外观模式

    定义

    • 外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性
    • 这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用
    • 外观模式就是将他们的关系放在一个Facade类中,降低了类类之间的耦合度

    实现

    • 实现类
    public class CPU {
        public void startup(){
            System.out.println("cpu startup!");
        }
    
        public void shutdown(){
            System.out.println("cpu shutdown!");
        }
    }
    public class Memory {
        public void startup(){
            System.out.println("memory startup!");
        }
        
        public void shutdown(){
            System.out.println("memory shutdown!");
        }
    }
    public class Disk {
        public void startup(){
            System.out.println("disk startup!");
        }
        
        public void shutdown(){
            System.out.println("disk shutdown!");
        }
    }
    
    • Facade类
    public class Computer {
        private CPU cpu;
        private Memory memory;
        private Disk disk;
    
        public Computer(){
            cpu = new CPU();
            memory = new Memory();
            disk = new Disk();
        }
    
        public void startup(){
            cpu.startup();
            memory.startup();
            disk.startup();
        }
    
        public void shutdown(){
            cpu.shutdown();
            memory.shutdown();
            disk.shutdown();
        }
    }
    
    • 测试
    public class Test {
        public static void main(String[] args) {
            Computer computer = new Computer();
            computer.startup();
            computer.shutdown();
        }
    }
    
    • 结果
    cpu startup!
    memory startup!
    disk startup!
        
    cpu shutdown!
    memory shutdown!
    disk shutdown!
    

    参考

    桥接模式

    定义

    • 桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。
    • 这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。
    • 桥接模式就是把事物和其具体实现分开,使他们可以各自独立的变化。桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化,像我们常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了。
    • 总的来说:实现接口作为一个维度,继承抽象类作为一个维度,俩者可以随意组合

    实现

    第一个维度

    • 创建桥接实现接口
    public interface DrawAPI {
       public void drawColor();
    }
    
    • 创建实现了 DrawAPI 接口的实体桥接实现类
    public class RedDraw implements DrawAPI {
       @Override
       public void drawColor() {
          System.out.println("color: red");
       }
    }
    
    public class GreenDraw implements DrawAPI {
       @Override
       public void drawColor() {
          System.out.println("color: green");
       }
    }
    

    第二个维度

    • 使用 DrawAPI 接口创建抽象类 Shape。
    public abstract class Shape {
        protected DrawAPI drawAPI;
        
        protected Shape(DrawAPI drawAPI){
            this.drawAPI = drawAPI;
        }
        
        public abstract void draw();  
    }
    
    • 实现了 Shape 接口的实体类
    public class Circle extends Shape {
        public Circle(DrawAPI drawAPI) {
            super(drawAPI);
        }
    
        public void draw() {
            System.out.println("Drawing Circle");
            drawAPI.drawColor();
        }
    }
    
    public class Rectangle extends Shape {
        public Rectangle(DrawAPI drawAPI) {
            super(drawAPI);
        }
    
        public void draw() {
            System.out.println("Drawing Rectangle");
            drawAPI.drawColor();
        }
    }
    
    • 测试
    public class BridgeDemo {
       public static void main(String[] args) {
          Shape redCircle = new Circle(new RedDraw());
          Shape greenRectangle = new Rectangle(new GreenDraw());
     
          redCircle.draw();
          greenRectangle.draw();
       }
    }
    
    • 结果
    Drawing Circle
    color: red
    
    Drawing Rectangle
    color: green
    

    组合模式

    定义

    • 组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
    • 这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。

    实现

    • 抽象构件
    abstract class Component {
        public abstract void operation();
        public void add(Component c) {
            throw new UnsupportedOperationException();
        }
        public void remove(Component c) {
            throw new UnsupportedOperationException();
        }
    }
    
    • UnsupportedOperationException 是为了 Leaf 在继承之后不用重写该方法,因为这些方法是 Composite 对象需要重写的,而单个对象不需要。
    • Leaf 类:
    class ConcreteComponent1 extends Component {
        public void operation() {
            System.out.println("operation of concrete component 1 ");
        }
    }
    
    class ConcreteComponent2 extends Component {
        public void operation() {
            System.out.println("operation of concrete component 2 ");
        }
    }
    
    class ConcreteComponent3 extends Component {
        public void operation() {
            System.out.println("operation of concrete component 3 ");
        }
    }
    
    • 组合类:
    class Composite extends Component {
        private ArrayList<Component> children;
        public Composite() {
            children = new ArrayList<Component>();
        }
        public void operation() {
            for(Component child: children) {
                child.operation();
            }
        }
    
        public void add(Component c) {
            children.add(c);
        }
    
        public void remove(Component c) {
            children.remove(c);
        }
    }
    
    • 调用:
    public class Client {
        public static void main(String[] args) {
            Component c1 = new ConcreteComponent1();
            Component c2 = new ConcreteComponent2();
            Component c3 = new ConcreteComponent3();
    
            Component c = new Composite();
            c.add(c1);
            c.add(c2);
            c.add(c3);
            c.operation();
    
            c.remove(c2);
            c.operation();
        }
    }
    
    • 运行结果:

    img

    参考

    享元模式

    定义

    • 享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。
    • 享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。

    典型享元模式

    • 典型享元类(享元:Flyweight,w不是大写)
    class Flyweight {
        //内部状态innerState作为成员变量,同一个享元对象其内部状态是一致的
        private String innerState;
        public Flyweight(String innerState) {
            this.innerState = innerState;
        }
        //外部状态outerState在使用时由外部设置,不保存在享元对象中,即使是同一个对象
        public void operation(String outerState) {
            //......
        }
    }
    
    • 典型享元工厂类
    class FlyweightFactory {
        //定义一个HashMap用于存储享元对象,实现享元池
        private HashMap flyweights = newHashMap();
        
        public Flyweight getFlyweight(String key){
            //如果对象存在,则直接从享元池获取
            if(flyWeights.containsKey(key)){
                return (Flyweight) flyweights.get(key);
            } else {
                //如果对象不存在,先创建一个新的对象添加到享元池中,然后返回
                Flyweight fw = newConcreteFlyweight();
                flyweights.put(key,fw);
                return fw;
            }
        }
    }
    

    通用写法

    • 解决某些场景频繁生成实例问题;使用泛型,节省写判断逻辑
    /**
    * 经典享元模式方法
    * 场景:解决某些场景频繁生成实例问题;使用泛型,节省写判断逻辑
    * 使用:String s = classicFlyweight(String.class);
    */
    private Map<String, Object> flyweightMap;
    private <T> T classicFlyweight(Class<T> clazz) {
        T t;
    
        if (flyweightMap == null)
            flyweightMap = new HashMap<>();
        String key = clazz.getName();
        if (flyweightMap.get(key) != null) {
            t = (T) flyweightMap.get(key);
        }else {
            try {
                t = clazz.newInstance();
                flyweightMap.put(key, t);
            } catch (Exception e) {
                t = null;
            }
        }
    
        return t;
    }
    

    连接池的实现

    • 数据库连接池
    • 通过连接池的管理,实现了数据库连接的共享,不需要每一次都重新创建连接,节省了数据库重新创建的开销,提升了系统的性能!
    public class ConnectionPool {
        private Vector<Connection> pool;
        
        //公有属性
        private String url = "jdbc:mysql://localhost:3306/test";
        private String username = "root";
        private String password = "root";
        private String driverClassName = "com.mysql.jdbc.Driver";
     
        private int poolSize = 100;
        private static ConnectionPool instance = null;
        Connection conn = null;
     
        //构造方法,做一些初始化工作 
        private ConnectionPool() {
            pool = new Vector<Connection>(poolSize);
            for (int i = 0; i < poolSize; i++) {
                try {
                    Class.forName(driverClassName);
                    conn = DriverManager.getConnection(url, username, password);
                    pool.add(conn);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
     
        //返回连接到连接池
        public synchronized void release() {
            pool.add(conn);
        }
     
        //返回连接池中的一个数据库连接
        public synchronized Connection getConnection() {
            if (pool.size() > 0) {
                Connection conn = pool.get(0);
                pool.remove(conn);
                return conn;
            } else {
                return null;
            }
        }
    }
    

    参考

    行为型模式

    思考

    策略模式和状态模式

    相同点

    • 两者通过将行为和状态拆分成一系列小的组件,由条件和状态进行功能更替,这样符合开闭原则,便于扩展。此外均可作为if else或者分支的替换方案;支持的最大行为和状态均有限;

    不同点

    • 策略模式中,类的功能是根据当前条件主动更改;
    • 状态模式中,类的功能是被动由当前状态更改;
    • 策略模式中每个行为或算法之间没有关联;
    • 状态模式中的状态之间有关联,并且状态本身控制着状态转移;

    解释器模式

    定义

    • 解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。

    实现

    • 创建一个表达式接口。
    public interface Expression {
        public boolean interpret(String context);
    }
    
    • 创建实现了上述接口的实体类。
    public class TerminalExpression implements Expression {
        private String data;
    
        public TerminalExpression(String data){
            this.data = data; 
        }
    
        @Override
        public boolean interpret(String context) {
            if(context.contains(data)){
                return true;
            }
            return false;
        }
    }
    public class OrExpression implements Expression {
        private Expression expr1 = null;
        private Expression expr2 = null;
    
        public OrExpression(Expression expr1, Expression expr2) { 
            this.expr1 = expr1;
            this.expr2 = expr2;
        }
    
        @Override
        public boolean interpret(String context) {      
            return expr1.interpret(context) || expr2.interpret(context);
        }
    }
    public class AndExpression implements Expression {
        private Expression expr1 = null;
        private Expression expr2 = null;
    
        public AndExpression(Expression expr1, Expression expr2) { 
            this.expr1 = expr1;
            this.expr2 = expr2;
        }
    
        @Override
        public boolean interpret(String context) {      
            return expr1.interpret(context) && expr2.interpret(context);
        }
    }
    
    • Test 使用 Expression 类来创建规则,并解析它们。
    public class Test {
        //规则:Robert 和 John 是男性
        public static Expression getMaleExpression(){
            Expression robert = new TerminalExpression("John");
            Expression john = new TerminalExpression("male");
            return new OrExpression(robert, john);    
        }
    
        //规则:Julie 是一个已婚的女性
        public static Expression getMarriedWomanExpression(){
            Expression julie = new TerminalExpression("Julie");
            Expression married = new TerminalExpression("Married");
            return new AndExpression(julie, married);    
        }
    
        public static void main(String[] args) {
            Expression isMale = getMaleExpression();
            Expression isMarriedWoman = getMarriedWomanExpression();
    
            System.out.println("John is male? " + isMale.interpret("John"));
            System.out.println("Julie is a married women? " 
                               + isMarriedWoman.interpret("Married Julie"));
        }
    }
    
    • 结果
    John is male? true
    Julie is a married women? true
    

    参考

    状态模式

    定义

    • 在状态模式(State Pattern)中,类的行为是基于它的状态改变的;我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。

    实现

    • 创建一个接口。
    public interface State {
        public void doAction(Context context);
    }
    
    • 俩个状态实现类.
    public class StartState implements State {
    
        public void doAction(Context context) {
            System.out.println("Player is in start state");
            context.setState(this); 
        }
    
        public String toString(){
            return "Start State";
        }
    }
    public class StopState implements State {
    
        public void doAction(Context context) {
            System.out.println("Player is in stop state");
            context.setState(this); 
        }
    
        public String toString(){
            return "Stop State";
        }
    }
    
    • 创建 Context 类。
    public class Context {
        private State state;
    
        public Context(){
            state = null;
        }
    
        public void setState(State state){
            this.state = state;     
        }
    
        public State getState(){
            return state;
        }
    }
    
    • 测试
    public class StatePatternDemo {
        public static void main(String[] args) {
            Context context = new Context();
    
            StartState startState = new StartState();
            startState.doAction(context);
    
            System.out.println(context.getState().toString());
    
            StopState stopState = new StopState();
            stopState.doAction(context);
    
            System.out.println(context.getState().toString());
        }
    }
    
    • 结果
    Player is in start state
    Start State
    Player is in stop state
    Stop State
    

    参考

    策略模式

    定义

    • 在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
    • 在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
    • 策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户.

    实现

    • 策略接口,定义策略执行接口
    public interface Strategy {
        int calculate(int a, int b);
    }
    
    • 具体策略类,实现策略接口,提供具体算法
    // 加法算法
    public class AddStrategy implements Strategy{
        @Override
        public int calculate(int a, int b) {
            return a + b;
        }
    }
    // 减法算法
    public class SubtractStrategy implements Strategy{
        @Override
        public int calculate(int a, int b) {
            return a - b;
        }
    }
    
    • 测试
    public class Test{
        public static void main(String[] args) {
            Strategy strategy = new AddStrategy();
            // 输出3
            System.out.println(strategy.calculate(1, 2));
            // 动态替换算法(策略)
            strategy = new SubtractStrategy();
            // 输出-1
            System.out.println(strategy.calculate(1, 2));
        }
    }
    
    • 测试main方法中,我们先使用"加法策略(算法)",然后调用calculate(1,2)方法得到结果3。然后动态替换策略为"减法策略(算法)",再次调用calculate(1,2)得到结果-1。

    参考

    观察者模式

    定义

    • 当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。

    实现

    • 抽象观察者接口(Observer)

      • 为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
    public interface Observer {
        //更新内容
        public void update(String msg);
    }
    
    • 观察者接口实现类

      • 实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态保持一致。
    public class TestObserver implements Observer{
        private String info;
    
        public TestObserver(String info){
            this.info = info;
        }
    
        @Override
        public void update(String msg) {
            System.out.println(info + "----" + msg);
        }
    }
    
    • 主题类

      • 将有关状态存入具体观察者对象;当具体主题内部状态放生改变时,通知所有注册过的观察者。
    public class TestSubject {
        private List<Observer> mList = new ArrayList();
        
        //添加观察者
        public void attach(Observer observer) {
            mList.add(observer);
        }
        
        //删除观察者
        public void detach(Observer observer) {
            mList.remove(observer);
        }
        
        //通知更新
        public void notify(String msg) {
            for (Observer observer : mList) {
                observer.update(msg);
            }
        }
    }
    
    • 测试
    public class TestMain {
        public static void main(String[] args) {
            Subject subject = new TestSubject();
    
            Observer observerA = new TestObserver("A:");
            Observer observerB = new TestObserver("B:");
            subject.attach(observerA);
            subject.attach(observerB);
            subject.notify("通知One");
            subject.detach(observerA);
            subject.notify("通知Two");
        }
    }
    
    • 日志打印

    image.png

    中介者模式

    定义

    • 中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。

    实现

    • 统一实现的行为
    public interface Mediator {
        public void showMessage(String msg);
    }
    
    • 创建 user 类。
    public class User implements Mediator{
        @Override
        public void showMessage(String message){
            System.out.println("User: " + message);
        }
    }
    
    • 创建中介类 - 负责传话
    public class ChatRoom implements Mediator{
        @Override
        public void showMessage(String message){
            (new User()).showMessage(message);
        }
    }
    
    • 测试
    public class Test {
       public static void main(String[] args) {
          ChatRoom John = new ChatRoom();
          John.showMessage("Hi! John!");
       }
    }
    
    • 结果
    User: Hi! John!
    

    备忘录模式

    定义

    • 备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。

    实现

    • Original类是原始类,里面有需要保存的属性value及创建一个备忘录类,用来保存value值。Memento类是备忘录类,Storage类是存储备忘录的类,持有Memento类的实例
    public class Original {
        public String value;
    
        public Original(String value) {
            this.value = value;
        }
    
        public Memento createMemento(){
            return new Memento(value);
        }
    
        public void restoreMemento(Memento memento){
            this.value = memento.value;
        }
    }
    
    public class Memento {
        public String value;
     
        public Memento(String value) {
            this.value = value;
        }
    }
    
    public class Storage {
        public Memento memento;
        
        public Storage(Memento memento) {
            this.memento = memento;
        }
    }
    
    • 测试
    public class Test {
        public static void main(String[] args) {
            // 创建原始类
            Original origi = new Original("egg");
            // 创建备忘录
            Storage storage = new Storage(origi.createMemento());
    
            // 修改原始类的状态
            System.out.println("初始化状态为:" + origi.value);
            System.out.println("修改后的状态为:" + origi.value = "niu");
    
            // 回复原始类的状态
            origi.restoreMemento(storage.memento);
            System.out.println("恢复后的状态为:" + origi.value);
        }
    }
    
    • 结果
    初始化状态为:egg
    修改后的状态为:niu
    恢复后的状态为:egg
    

    参考

    命令模式

    定义

    • 命令模式(Command Pattern)是一种数据驱动的设计模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。

    实现

    • 命令接口
    public interface Order {
        public void execute();
    }
    
    • 创建执行请求命令的实体类。
    public class BuyStock implements Order {
        private String name = "ABC";
       	private int quantity = 10;
    
        @Override
        public void execute() {
            System.out.println("Stock [ Name: "+name+", Quantity: " + quantity +" ] bought");
        }
    }
    
    • 创建命令调用类。
    public class Broker {
        private List<Order> orderList = new ArrayList<Order>(); 
    
        public void takeOrder(Order order){
            orderList.add(order);      
        }
    
        public void placeOrders(){
            for (Order order : orderList) {
                order.execute();
            }
            orderList.clear();
        }
    }
    
    • 测试
    public class CommandPatternDemo {
        public static void main(String[] args) {
            Stock abcStock = new Stock();
    
            BuyStock buyStockOrder = new BuyStock(abcStock);
            Broker broker = new Broker();
            broker.takeOrder(buyStockOrder);
    
            broker.placeOrders();
        }
    }
    
    • 结果
    Stock [ Name: ABC, Quantity: 10 ] bought
    

    参考

    责任链模式

    定义

    • 责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。

      责任链会将特定行为转换为被称作处理者的独立对象。 每个检查步骤都可被抽取为仅有单个方法的类, 并执行检查操作。 请求及其数据则会被作为参数传递给该方法。

      该模式建议你将这些处理者连成一条链。 链上的每个处理者都有一个成员变量来保存对于下一处理者的引用。 除了处理请求外, 处理者还负责沿着链传递请求。 请求会在链上移动, 直至所有处理者都有机会对其进行处理。

      最重要的是: 处理者可以决定不再沿着链传递请求, 这可高效地取消所有后续处理步骤。

    实现

    • 创建抽象的记录器类
    public abstract class AbstractLogger {
        public static int INFO = 1;
        public static int DEBUG = 2;
        public static int ERROR = 3;
        protected int level;
    
        //责任链中的下一个元素
        protected AbstractLogger nextLogger;
    
        public void setNextLogger(AbstractLogger nextLogger){
            this.nextLogger = nextLogger;
        }
    
        public void logMessage(int level, String message){
            if(this.level <= level){
                write(message);
            }
            if(nextLogger != null){
                nextLogger.logMessage(level, message);
            }
        }
    
        abstract protected void write(String message);
    }
    
    • 创建扩展了该记录器类的实体类
    public class ConsoleLogger extends AbstractLogger {
        public ConsoleLogger(int level){
            this.level = level;
        }
    
        @Override
        protected void write(String message) {    
            System.out.println("Standard Console::Logger: " + message);
        }
    }
    
    public class ErrorLogger extends AbstractLogger {
        public ErrorLogger(int level){
            this.level = level;
        }
    
        @Override
        protected void write(String message) {    
            System.out.println("Error Console::Logger: " + message);
        }
    }
    
    public class FileLogger extends AbstractLogger {
        public FileLogger(int level){
            this.level = level;
        }
    
        @Override
        protected void write(String message) {    
            System.out.println("File::Logger: " + message);
        }
    }
    
    • 测试
    public class Test {
        private static AbstractLogger getChainOfLoggers(){
            AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
            AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
            AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);
    
            errorLogger.setNextLogger(fileLogger);
            fileLogger.setNextLogger(consoleLogger);
            return errorLogger;  
        }
    
        public static void main(String[] args) {
            AbstractLogger loggerChain = getChainOfLoggers();
    
            loggerChain.logMessage(AbstractLogger.INFO, "This is an information.");
            loggerChain.logMessage(AbstractLogger.DEBUG, "This is a debug level information.");
            loggerChain.logMessage(AbstractLogger.ERROR, "This is an error information.");
        }
    }
    
    • 结果
    Standard Console::Logger: This is an information.
    File::Logger: This is a debug level information.
    Standard Console::Logger: This is a debug level information.
    Error Console::Logger: This is an error information.
    File::Logger: This is an error information.
    Standard Console::Logger: This is an error information.
    

    参考

    访问者模式

    定义

    • 在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。

    实现

    • Visitor类,存放要访问的对象
    public interface Visitor {
        public void visit(Subject sub);
    }
    
    public class MyVisitor implements Visitor {
        @Override
        public void visit(Subject sub) {
            System.out.println("visit the subject:"+sub.getSubject());
        }
    }
    
    • Subject类,accept方法,接受将要访问它的对象,getSubject()获取将要被访问的属性
    public interface Subject {
        public void accept(Visitor visitor);
        public String getSubject();
    }
    
    public class MySubject implements Subject {
        @Override
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }
    
        @Override
        public String getSubject() {
            return "love";
        }
    }
    
    • 测试
    public class Test {
        public static void main(String[] args) {
            Visitor visitor = new MyVisitor();
            Subject sub = new MySubject();
            sub.accept(visitor);    
        }
    }
    
    • 结果:visit the subject:love

    参考

    迭代器模式

    定义

    • 迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式。这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。

    实现

    • 创建接口
    public interface Iterator {
        public boolean hasNext();
        public Object next();
    }
    public interface Container {
        public Iterator getIterator();
    }
    
    • 创建实现了 Container 接口的实体类。该类有实现了 Iterator 接口的内部类 NameIterator。
    public class NameRepository implements Container {
        public String names[] = {"Robert" , "John" ,"Julie" , "Lora"};
    
        @Override
        public Iterator getIterator() {
            return new NameIterator();
        }
    
        private class NameIterator implements Iterator {
    
            int index;
    
            @Override
            public boolean hasNext() {
                if(index < names.length){
                    return true;
                }
                return false;
            }
    
            @Override
            public Object next() {
                if(this.hasNext()){
                    return names[index++];
                }
                return null;
            }     
        }
    }
    
    • 测试
    public class Test {
        public static void main(String[] args) {
            NameRepository namesRepository = new NameRepository();
            for(Iterator iter = namesRepository.getIterator(); iter.hasNext();){
                String name = (String)iter.next();
                System.out.println("Name : " + name);
            }  
        }
    }
    
    • 结果
    Name : Robert
    Name : John
    Name : Julie
    Name : Lora
    

    参考

    模板模式

    定义

    • 在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
    • 超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤

    实现

    • 创建一个抽象类
    public abstract class Game {
        abstract void initialize();
        abstract void startPlay();
        abstract void endPlay();
    
        //模板
        public final void play(){
            //初始化游戏
            initialize();
            //开始游戏
            startPlay();
            //结束游戏
            endPlay();
        }
    }
    
    • 实现类
    public class Football extends Game {
        @Override
        void endPlay() {
            System.out.println("Football Game Finished!");
        }
    
        @Override
        void initialize() {
            System.out.println("Football Game Initialized! Start playing.");
        }
    
        @Override
        void startPlay() {
            System.out.println("Football Game Started. Enjoy the game!");
        }
    }
    
    • 测试
    public class Test {
        public static void main(String[] args) {
            Game game = new Football();
            game.play();      
        }
    }
    
    • 结果
    Football Game Initialized! Start playing.
    Football Game Started. Enjoy the game!
    Football Game Finished!
    

    参考

    最后

    • 文章地址:study-notes
      • 提供良好的搬运格式,搬运使用时,请附上引用出处
  • 相关阅读:
    Linux内核分析
    socket的protocal参数
    linux修改系统时间
    asdfadsf
    NoSQL数据库笔谈
    scrapy安装
    数字证书及CA的扫盲介绍
    Java数据类型
    EXISTS的用法
    python链接
  • 原文地址:https://www.cnblogs.com/xdd666/p/14025132.html
Copyright © 2011-2022 走看看