• 【设计模式4_建造者、装饰者、代理】


    建造者

    有如下属性很多的对象:

    public class Product {
    
        private final String number;
    
        private final String price;
    
        private final String count;
    	
    	// .. 其他很多属性
    
        public Product(String number, String price, String count) {
            this.number = number;
            this.price = price;
            this.count = count;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    那么我们每次使用它时,都自能通过有参构造器,并且要一次传入全部参数,而当下有不同的业务场景,在某些场景下这个Product的属性是我们不需要的,而针对每一种场景不同属性排列组合都要提供构造方法,在大对象下构造器数量都会是几何倍数的增长。

    就可以使用建造者模式,对这个类创建一个Builder类(通常是静态内部类):

    static class Builder {
    
            private String number;
            private String price;
            private String count;
    
    
            Builder buildNumber (String number){
                this.number = number;
                return this;
            }
    
            Builder buildPrice (String price){
                this.price = price;
                return this;
            }
    
            Builder buildCount (String count){
                this.count = count;
                return this;
            }
    
    
            Product build(){
                // 做一些校验,根据不同场景赋默认值等
                if (this.price == null){
                    // 必须赋值的属性,则抛出异常...
                }
                if (this.number == null){
                    this.number = "1";
                }
                if (this.count == null || "".equals(this.count)){ // 如果空,就通过计算得出
                    this.count = new BigDecimal(this.number).multiply(new BigDecimal(this.price)).toString();
                }
    
                return new Product(this.number,this.price,this.count);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    使用时,可以只关注当前所需的属性,灵活地构造出相应对象:

          Product build1 = new Product.Builder().buildPrice("3.14").buildNumber("100").buildCount("1000").build();
          Product build2 = new Product.Builder().buildPrice("3.14").buildNumber("100").build();
          Product build3 = new Product.Builder().buildPrice("3.14").build();
    
          System.out.println(build1);// Product{number='100', price='3.14', count='1000'}
          System.out.println(build2);// Product{number='100', price='3.14', count='314.00'}
          System.out.println(build3);// Product{number='1', price='3.14', count='3.14'}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    装饰者

    在不改变原有对象的前提下,对原来的功能进行扩展。

    简单的场景可以直接继承父类,然后覆写需要扩展的方法即可

    
    class AAA {
     	public AAA execute(){
     		//...
     	}
    }
    class BBB extends AAA {
    	public AAA execute(){
    		//... BBB做自己的扩展
    		super.execute();
     	}
    }
    
    class CCC extends AAA {
    	public AAA execute(){
    		//... CCC做自己的扩展
    		super.execute();
     	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    java类的继承,显得很臃肿,装饰者模式是在不改变原有类文件和使用继承的情况下,通过创建一个包装对象动态地扩展一个对象的功能,相比直接创建子类更为灵活。

    
    class AAA {
     	public AAA execute(){
     		//...
     	}
    }
    class BBB extends AAA {
    
    	private AAA aaa;
    	
    	public BBB(AAA aaa){
    		this.aaa = aaa;
    	}
    	
    	public AAA execute(){
    		//... BBB做自己的扩展
    		this.aaa.execute();
     	}
    }
    
    class CCC extends AAA {
    
    	private AAA aaa;
    	
    	public CCC(AAA aaa){
    		this.aaa = aaa;
    	}
    	
    	public AAA execute(){
    		//... CCC做自己的扩展
    		this.aaa.execute();
     	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    装饰者模式下,对于我已经拿到的aaa对象,直接传到BBB的构造器中即可;但在继承下,要么强制类型转换为bbb对象,要么把属性挨个赋值再创建。
    简言之,装饰者模式可以直接使用现有aaa对象,交给bbb包装。而继承适用在没有现有aaa对象前提下,创建bbb,(当然也会引发父类构造器创建出新的aaa)。装饰者更灵活。

    在IO流中,字符缓冲输入流: new BufferedReader(new InputStreamReader(new FileInputStream()))

    代理模式

    代理模式:为其他对象提供一种代理以控制对这个对象的访问。代理模式是一项基本的设计技巧,许多其他的模式,如状态模式、策略模式、访问者模式本质上也采用了代理模式。

    代理模式和装饰者模式又有很多相似的地方,但代理模式强调控制访问,而装饰者模式强调扩展功能。

    代码上的直观区别:

    // 目标类,被代理或被装饰的类
    class AAA {
     	public void execute(){
     		//...
     	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    /*
    * 装饰者模式。只提供接收aaa对象的构造器,直接对客户端现有的aaa做包装。
    */
    
    class BBB {
    
    	private AAA aaa;
    	
    	public BBB(AAA aaa){
    		this.aaa = aaa;
    	}
    	
    	public void execute(){
    		//... BBB做自己的扩展
    		this.aaa.execute();
     	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    
    /*
    * 代理模式。由代理类中创建出aaa,而对客户端是无感知。客户端也不需要明确如何创建aaa。
    */
    
    class BBB {
    
    	private AAA aaa;
    	
    	public BBB(){
    		this.aaa = new AAA();
    	}
    	
    	public void execute(){
    		//... BBB做自己的扩展
    		this.aaa.execute();
     	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    代理模式的访问控制主要在于对目标类的无感知访问,也就是说客户端不需要知道被代理类的具体情况,只需要知道代理类可以帮助我们完成想要的功能就对了;目标类的创建由代理类负责并对客户端隐藏。

    装饰者模式,客户端是自己有一个目标类的对象的,只是这个对象不能满足我们的要求,需要对其进行扩展功能。

    代理对象与真实对象的关系在编译时就确定,bbb只能代理特定的一种aaa;而装饰者在运行时构造,无论什么样的aaa对象都可以交给bbb装饰。

    适配器模式

    将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。

    两种方式:组合 和 继承。

    1. 组合

    组合的方式看起来很像是将不匹配的接口"装饰"成期望的接口。
    同样也是将目标类对象拿到适配器类中作为一个成员变量,并通过构造器传入现成的目标类对象完成初始化。
    再通过一个方法:使用目标类的方法完成一部分逻辑,再和自己的逻辑"组合"出满足客户端的方法。

    public class Adapter {
    	private Adaptee adaptee;
    
    	public Adapter(Adaptee adaptee) {
    		this.adaptee=adaptee;
    	}
    
    	public void adapte() {
    		adaptee.target();
    		// ...自己的逻辑
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    适配器模式与装饰器模式的区别
    装饰器与适配器都有一个别名叫做 包装模式(Wrapper),它们都是起到包装一个类或对象的作用,但是使用它们的目的不一样。

    适配器模式的意义是要将一个接口转变成另一个接口,它的目的是通过改变接口来达到重复使用的目的。(方法签名要变)

    而装饰器模式不是要改变被装饰对象的接口,而是恰恰要保持原有的接口,增强原有对象的功能,且对装饰类的使用者透明。(方法签名不变,对客户端无感知)

    例如BuffedReader 中readLine() 方法对 InputStreamReader中 read()方法的适配?

    1. 继承
      本质上是利用多实现,将不同接口的方法逻辑“组合”在一个方法中。
      继承有数量上限,多重继承后类的关系复杂。
      客户端不用自行实例化目标类。
      对接口有污染,本不该出现的、用不上的方法也会出现在行为列表中。

    门面模式(外观模式)- Facade

    就是把一系列的接口,在门面类中合并到一起作为统一对外的入口,客户端不再原本一系列接口如何使用,只需要通过门面调用即可。

    其实Service中完成多个Mapper调用,组合成一个方法。就是门面模式的简单实现了。

    org.apache.catalina.connector.RequestFacade

    享元模式 (轻量级模式) - Flyweight Pattern

    运用共享技术有效地支持大量细粒度的对象,将多个对同一对象的访问集中起来,不必为每个访问者创建一个单独的对象,以此来降低内存的消耗。

    尝试复用现有的同类对象,如果未找到匹配的对象,则创建新对象,并新加入缓存。主要用于减少创建对象的数量,以减少内存占用和提高性能。其本质是缓存共享对象,降低内存消耗。

    享元模式其实就是工厂模式的一个改进机制,享元模式同样要求创建一个或一组对象,并且就是通过工厂方法生成对象的,只不过享元模式中为工厂方法增加了缓存这一功能。

    
    public class TicketFactory {
        
        private static Map<String, ITicket> ticketPool = new ConcurrentHashMap<String, ITicket>();
        
        public static ITicket queryTicket(String from, String to, String type) {
        
            if (ticketPool.containsKey(key)) {
    
                return ticketPool.get(key);
            }
    
    		// 不存再则创建,并加入到缓存。(这一步就是对工厂的改进和不同之处)
            ITicket ticket = new TrainTicket(from, to, type);
            ticketPool.put(key, ticket);
            return ticket;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    享元模式实现数据库连接池:https://www.yisu.com/zixun/731423.html
    经常使用的数据库连接池也使用了享元模式,有效提高了其运行的性能。

    在使用数据库连接池时,由于经常使用Connection对象,其主要性能消耗在建立连接和关闭连接的时候,为了提高Connection在调用时的性能,可以将Connection对象在调用前创建好缓存起来,用的时候从缓存中取值,用完再放回去,达到资源重复利用的目的。

  • 相关阅读:
    SpringCloud案例day03.md
    可以在家干的兼职都有哪些呢?做自媒体怎么样?
    爱普生LQ1900KIIH复位方法
    快手API接口解析,实现根据ID取商品详情
    【概率论】关于为什么样本标准偏差分母是n-1的进一步理解
    springboot+vue学生综合测评系统(java项目源码+文档)
    MIGO新增页签增强
    SOLIDWORKS Visualize 界面介绍
    数据结构学习笔记(一)——基本概念
    2022年第一季度保险服务数字化跟踪分析
  • 原文地址:https://blog.csdn.net/qq_45494908/article/details/126919452