• 2020-03-09


    庚子鼠年 己卯月 辛亥日

    描述

    设计模式学习

    技术博客:

    随笔

    建造者模式

    使用场景

    • 当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用构造者模式。

    解决的问题

    当一个类的构造函数参数超过4个,而且这些参数有些是可选的时,我们通常有两种办法来构建它的对象。 例如我们现在有如下一个类计算机类Computer,其中cpu与ram是必填参数,而其他3个是可选参数,那么我们如何构造这个类的实例呢,通常有两种常用的方式:

    • 第一:折叠构造函数模式(telescoping constructor pattern ),这个我们经常用,如下代码所示(写很多构造方法)

    • 第二种:Javabean 模式(用set刚刚传值)

    那么这两种方式有什么弊端呢? 第一种主要是使用及阅读不方便。你可以想象一下,当你要调用一个类的构造函数时,你首先要决定使用哪一个,然后里面又是一堆参数,如果这些参数的类型很多又都一样,你还要搞清楚这些参数的含义,很容易就传混了。。。那酸爽谁用谁知道。 第二种方式在构建过程中对象的状态容易发生变化,造成错误。因为那个类中的属性是分步设置的,所以就容易出错。

    为了解决这两个痛点,builder模式就横空出世了。

    如何实现

    传统模式
    1. 建造者(Builder):为创建一个产品对象的各个部件指定抽象接口。
    2. 具体建造者(ConcreteBuilder):实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,并 提供一个检索产品的接口。
    3. 指挥者(Director):指挥并构造一个使用Builder接口的对象。
    4. 产品(Product):表示被构造的复杂对象(具体的实现类)。ConcreteBuilder创建该产品的内部表示并定义它的装配过程,包含定义组成部件的类,包括将这些部件装配成最终产品的接口。
    静态内部类(推荐)
    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 String cpu;//必须
        private String ram;//必须
        private int usbCount;//可选
        private String keyboard;//可选
        private 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方法
            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方法
            public Computer build(){
                return new Computer(this);
            }
        }
    }
    

    参考文献:https://zhuanlan.zhihu.com/p/58093669

    适配器模式

    • 适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。

    • 在适配器模式中,我们通过增加一个新的适配器类来解决接口不兼容的问题,使得原本没有任何关系的类可以协同工作。

    • 根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器两种,在对象适配器模式中,适配器与适配者之间是关联关系;在类适配器模式中,适配器与适配者之间是继承(或实现)关系。

    • 角色
      Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。

    Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。

    Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。

    类适配器

    // 被适配者的方法
    public class Adaptee {
        public void adapteeRequest() {
            System.out.println("被适配者的方法");
        }
    }
    
    // 适配器接口
    public interface Target {
        void request();
    }
    
    // 适配器
    public class Adapter extends Adaptee implements Target{
        @Override
        public void request() {
            //  ...一些操作...
            super.adapteeRequest();
            //  ...一些操作...
        }
    }
    

    对象适配器

    // 适配器
    public class Adapter implements Target{
        
        private Adaptee adaptee;
        
        public Target(Adaptee adaptee){
            this.adaptee = adaptee;
        }
        
        
        @Override
        public void request() {
            //  ...一些操作...
            adaptee.adapteeRequest();
            //  ...一些操作...
        }
    }
    

    接口适配器

    //目标接口,有多个方法
    public interface IDCOutput {
        public int output5V();
        public int output12V();
        public int output20V();
    }
    //中间类,空实现所有方法,这是一个抽象类
    public abstract class DefaultAdapter implements IDCOutput {
        @Override
        public int output5V() {
            return 0;
        }
    
        @Override
        public int output12V() {
            return 0;
        }
    
        @Override
        public int output20V() {
            return 0;
        }
    }
    //我的mac电源适配器只需要实现20V的方法即可
    public class MacAdatper extends DefaultAdapter {
    
        private AC ac;
    
        public MacAdatper(AC ac){
            this.ac = ac;
        }
    
        @Override
        public int output20V() {
            return ac.outputAC()/11;
        }
    
        public static void main(String[] args) {
            MacAdatper adatper = new MacAdatper(new AC());
            System.out.println("mac电脑电压:" + adatper.output20V());
        }
    }
    //输出结果:
    //mac电脑电压:20
    

    适配器模式总结

    主要优点:

    • 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。

    • 增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用。

    • 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。

    具体来说,类适配器模式还有如下优点:

    • 由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。
      对象适配器模式还有如下优点:

    • 一个对象适配器可以把多个不同的适配者适配到同一个目标;

    • 可以适配一个适配者的子类,由于适配器和适配者之间是关联关系,根据“里氏代换原则”,适配者的子类也可通过该适配器进行适配。

    类适配器模式的缺点如下:

    • 对于Java、C#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者;
    • 适配者类不能为最终类,如在Java中不能为final类,C#中不能为sealed类;
    • 在Java、C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性。

    对象适配器模式的缺点如下:

    • 与类适配器模式相比,要在适配器中置换适配者类的某些方法比较麻烦。如果一定要置换掉适配者类的一个或多个方法,可以先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。

    适用场景:

    • 系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码。
    • 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。

    参考博客:https://blog.csdn.net/wwwdc1012/article/details/82780560

  • 相关阅读:
    经典布局 ---- 双飞翼
    细嚼浏览器兼容----条件注释判断浏览器版本
    webqq的注册登记和聊天页面--运用jsonp跨域
    Bootstrap框架的要点--栅格系统
    html5橡皮檫特效
    PHP正确获取客户端IP地址
    常用排序算法及Java实现
    Math类中的floor、ceil和round方法
    Java中的动态反射机制和动态代理
    测试
  • 原文地址:https://www.cnblogs.com/chang1024/p/12451529.html
Copyright © 2020-2023  润新知