zoukankan      html  css  js  c++  java
  • 设计模式07-享元模式与组合模式详解

    1.7.设计模式07-享元模式与组合模式详解

    1.7.1.享元模式详解

     时长:1h12min

    学习目标:

    》掌握享元模式与组合模式的应用场景

    》 了解享元模式的内部状态和外部状态

    》掌握组合模式的透明写法和安全写法

    》享元模式和组合模式的优缺点

    7.1.享元模式的基本定义

    定义

      Flyweight Pattern,享元模式。又称轻量级模式,是对象池的一种实现,类似于线程池,线程池可以避免不停

    地创建和销毁多个对象,消耗性能。通过减少对象数量从而改善应用所需的对象结构的方式。

    宗旨:

      共享细粒度对象,将多个对同一对象的访问集中起来。来达到资源的重复利用。

      属于结构型模式。   

    享元模式在生产中的体现:

    房产中介:各类房源共享,全国社保联网。

    7.1.1.应用场景

    常常应用于系统底层的开发,以便解决系统性能问题。

    系统有大量相似对象,需要缓冲池的场景。

    7.2.享元模式的code实现

    7.2.1.通用写法
    1.顶层享元接口定义

    一般需要传入外部状态

    package com.wf.flyweight.general;
    
    /**
     * @ClassName IFlyweight
     * @Description 享元角色,顶层接口
     * @Author wf
     * @Date 2020/5/25 10:07
     * @Version 1.0
     */
    public interface IFlyweight {
        void operation(String extrinsicState);
    }
    2.定义具体的享元实现
    package com.wf.flyweight.general;
    
    /**
     * @ClassName ConcreteFlyweight
     * @Description 享元实现,因为该类实例需要反复使用,所以,使用工厂类来创建实例
     * @Author wf
     * @Date 2020/5/25 10:10
     * @Version 1.0
     */
    public class ConcreteFlyweight implements IFlyweight {
        private String intrinsicState;
    
        public ConcreteFlyweight(String intrinsicState) {
            this.intrinsicState = intrinsicState;
        }
    
        @Override
        public void operation(String extrinsicState) {
            System.out.println("Object address:"+System.identityHashCode(this));
            System.out.println("IntrinsicState:"+this.intrinsicState);
            System.out.println("ExtrinsicState:"+extrinsicState);
        }
    }
    3.享元工厂
    package com.wf.flyweight.general;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * @ClassName FlyweightFactory
     * @Description 享元工厂,因为需要把实例添加到缓存,会创建容器
     * @Author wf
     * @Date 2020/5/25 10:16
     * @Version 1.0
     */
    public class FlyweightFactory {
        private static Map<String,IFlyweight> pool = new HashMap<String,IFlyweight>();
    
        //因为内部状态具备不变性,因此,作为缓存key
        public static IFlyweight getFlyweight(String intrinsicState){
            if(!pool.containsKey(intrinsicState)){
                IFlyweight flyweight = new ConcreteFlyweight(intrinsicState);
                pool.put(intrinsicState, flyweight);
            }
            return pool.get(intrinsicState);
        }
    }

    注意:

      享元对象是多例的

    4.系统类图

    7.2.2.享元模式的实际使用

      在外地工作的同学,每年过春节回家,为了购买火车票,都需要提前一个月进行抢票。如果人工抢票,一般官方放票一两小时,

    车票就会抢光了,这时出现很多刷票软件

    刷新软件的实现原理:

      会定时检查12306的余票数量【通过sdk或爬虫】,一旦发现有余票,就会把它放到共享池里面,就可以让大家一起来抢票了。

      在抢票之前,需要先缓存抢票人的个人信息,出发站,目的地,如果要求匹配,就立马下单,就不需要重复填写信息。就能加快抢票

    速度,提高效率。

      下面用代码来模拟抢票的过程。

     7.2.2.1.创建顶层享元抽象接口
    ackage com.wf.flyweight.ticket;
    
    /**
     * @ClassName ITicket
     * @Description 火车票,享元接口
     * @Author wf
     * @Date 2020/5/25 10:45
     * @Version 1.0
     */
    public interface ITicket {
        //根据席别区分车票
        void showInfo(String bunk);
    }
    7.2.2.2.享元具体实现
    package com.wf.flyweight.ticket;
    
    import java.util.Random;
    
    /**
     * @ClassName TrainTicket
     * @Description 火车票,具体享元实现
     * @Author wf
     * @Date 2020/5/25 10:47
     * @Version 1.0
     */
    public class TrainTicket implements ITicket {
        //列车出发地
        private String from;
        //目的地
        private String to;
        //价格由后台设定,用户无需填写
        private int price;
    
        public TrainTicket(String from, String to) {
            this.from = from;
            this.to = to;
        }
    
        @Override
        public void showInfo(String bunk) {
            this.price = new Random().nextInt(500);
            System.out.println(from+"->"+to+":"+ bunk + ",价格是:"+this.price);
    
        }
    }
    7.2.2.3.享元类创建工厂
    package com.wf.flyweight.ticket;
    
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    /**
     * @ClassName TicketFactory
     * @Description 车票创建工厂
     * @Author wf
     * @Date 2020/5/25 10:55
     * @Version 1.0
     */
    public class TicketFactory {
        private static Map<String,ITicket> pool = new ConcurrentHashMap<String,ITicket>();
        public static ITicket queryTicket(String from, String to){
            String key = from +"->"+ to;
            if(!pool.containsKey(key)){
                ITicket ticket = new TrainTicket(from,to);
                System.out.println("首次查询,创建对象:"+key);
                pool.put(key,ticket);
            }
    
            return pool.get(key);
        }
    }
    7.2.2.4.客户调用
    package com.wf.flyweight.ticket;
    
    /**
     * @ClassName Test
     * @Description 客户端调用
     * @Author wf
     * @Date 2020/5/26 16:23
     * @Version 1.0
     */
    public class Test {
        public static void main(String[] args) {
            ITicket ticket = TicketFactory.queryTicket("杭州","遵义");
            ticket.showInfo("硬座");
        }
    
    }

    测试结果如下:

     

    类图如下所示:

    说明:

      享元模式,看起来和注册工单例是很像的。然而享元模式的重点,在于系统结构上,而不关心对象创建,是不是单例的。

    7.2.3.享元模式应用之数据库连接池
    7.2.3.1.顶层享元抽象接口

      这个接口已经存在了,可以直接使用jdk提供的Connection接口。

    7.2.3.1.享元工厂
    package com.wf.flyweight.pool;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.util.Vector;
    
    /**
     * @ClassName ConnectionPool
     * @Description TODO
     * @Author wf
     * @Date 2020/5/26 16:54
     * @Version 1.0
     */
    public class ConnectionPool {
        private Vector<Connection> pool;
        private int poolSize = 100;
        private String url = "jdbc:mysql://localhost:3306/test";
        private String username = "root";
        private String password = "root";
        private String driverClassName = "com.mysql.jdbc.Driver";
    
        public ConnectionPool() {
            this.pool = new Vector<Connection>(poolSize);
            try{
                Class.forName(driverClassName);
                for(int i=0;i< poolSize;i++) {
                    Connection conn = DriverManager.getConnection(url, username, password);
                    pool.add(conn);
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    
        public synchronized Connection getConnection(){
            if(poolSize > 0){
                Connection conn = pool.get(0);
                pool.remove(conn);
                return conn;
            }
            return null;
        }
    
        public synchronized void release(Connection conn){
            pool.add(conn);
        }
    }
     7.2.3.2.测试类
    package com.wf.flyweight.pool;
    
    
    import java.sql.Connection;
    
    /**
     * @ClassName Test
     * @Description 测试类
     * @Author wf
     * @Date 2020/5/26 17:12
     * @Version 1.0
     */
    public class Test {
        public static void main(String[] args) {
            ConnectionPool pool = new ConnectionPool();
            Connection conn = pool.getConnection();
            System.out.println(conn);
    
        }
    }

     测试要想成功,需要本地安装mysql服务器,并且存在test数据库。

    还需要添加mysql pom依赖:

     <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>5.1.38</version>
        </dependency>

    测试结果如下:

     7.3.享元模式在源码中的应用

    7.3.1.jdk String使用享元模式
    package com.wf.flyweight.jdk;
    
    /**
     * @ClassName StringTest
     * @Description String源码研究测试
     * @Author wf
     * @Date 2020/5/26 17:37
     * @Version 1.0
     */
    public class StringTest {
        public static void main(String[] args) {
            //这句声明,创建了几个对象?
            //s1是在运行阶段声明的,“hello”字符串常量,在编译期就已经完成创建
            //常量存储在常量池
            String s1= "hello";
    
            String s2= "hello";
            System.out.println(s1 == s2);//true,两个变量都从常量池取值
    
            //测试:反例
            //两个常量,拼接,在编译阶段就会,得到最终常量,jdk的底层做了优化处理
            String s3 = "he" + "llo";
            System.out.println(s1 == s3);//true
    
            String s4 ="hel" + new String("lo");
            System.out.println(s1 == s4);//false
    
            String s5 = new String("hello");
            System.out.println(s1 == s5);//false
            System.out.println(s4 == s5);//false
    
            String s6 = s5.intern();//从缓存中取值
            System.out.println(s1 == s6);//true
    
            String s7 = "h";
            String s8 = "ello";
            String s9 = s7 + s8;
            System.out.println(s1 == s9);//false
    
    
        }
    }
    7.3.2.Integer使用享元模式
    package com.wf.flyweight.jdk;
    
    /**
     * @ClassName IntegerTest
     * @Description 测试享元模式应用
     * @Author wf
     * @Date 2020/5/26 18:43
     * @Version 1.0
     */
    public class IntegerTest {
        public static void main(String[] args) {
            Integer a = Integer.valueOf(127);
            Integer b = 127;
            System.out.println(a == b);//true
    
            Integer c = Integer.valueOf(128);
            Integer d = 128;
            System.out.println(c == d);//false
        }
    }
    注意:
      这里根据经验值,i在【-128,127】之间时,才会缓存值。

      特殊处理是在valueOf中进行的。

    7.3.3.Long使用享元模式

    ValueOf中进行处理。

    7.4.享元模式扩展知识

    7.4.1.内部状态和外部状态

      享元模式,提出两个要求。一是细粒度,一是共享。

      细粒度对象,不可避免就会产生很多对象。

      内部状态:是享元状态共享出来的东西,它存在享元对象内部,不会跟随环境改变而改变。

      外部状态:是指对象得以依赖的一个标记,会随着环境变化而改变。是不可共享的状态。

    7.4.2.享元模式的优点和缺点

    优点:

      减少对象的创建,降低内存中对象的数量,降低系统内存消耗,提高效率。

      减少内存之外的其他资源占用。

    缺点:

      关注内部 ,外部状态,关注线程安全问题

      使系统,程序的逻辑复杂化

    7.4.3.享元模式与其他设计模式的关联
    7.4.3.1.享元模式与代理模式

    静态代理:持有目标对象的引用,是一对一的关系。达到功能增强的目的。

    享元模式:也会持有目标对象的引用,是一对多的关系。提高效率,达到资源复用的目的。

    7.4.3.2.享元模式与单例模式

    单例模式:单例强调对象的创建是否唯一。

    享元模式:通常结合工厂模式使用,一般会把享元类设计为单例类。它强调的是系统结构。

     1.7.2.组合模式详解

    时长:60min

    7.2.1.组合模式的定义

    定义

      Composite Pattern,也称整体-部分模式,它的宗旨是通过将单个对象(叶子节点)和组合对象(树枝节点)

    用相同的接口进行表示。属于结构型模式。

    作用:

      使客户端对单个对象和组合对象保持一致的方式处理。

      

    组合/聚合区分:

      心在一起,称为团队,也可称组合。如:人的组合,身体和头,相互依存,不能分割,这称为组合。

      只是人在一起,称团伙,也可称聚合。而像选择授课老师,这种可以灵活选择,称为聚合。U盘和电脑,也是一种聚合关系。

    7.2.1.1.生活中组合模式的体现

      》公司组织架构

      》操作系统的文件管理系统

    7.2.1.2.组合模式的适用场景

    1.希望客户端可以忽略组合对象与单个对象的差异时

    2.对象层次具备整体与部分,呈树形结构(如:树形菜单,操作系统目录,公司组织架构等)

    7.2.2.组合模式的代码示例

    7.2.2.1.通用写法

    组合模式的通常实现,又可分为两种写法:透明写法,和安全写法

    1.透明写法

    A.抽象组件

      也是一个根节点,代码如下:

    package com.wf.composite.transparent;
    
    /**
     * @ClassName Component
     * @Description 组合对象,顶层抽象组件,根节点
     * @Author wf
     * @Date 2020/5/27 10:00
     * @Version 1.0
     */
    public abstract class Component {
        protected String name;
    
        public Component(String name) {
            this.name = name;
        }
    
        public abstract String operation();
    
        public boolean addChild(Component component){
            throw new UnsupportedOperationException("addChild not supported!");
        }
        public boolean removeChild(Component component){
            throw new UnsupportedOperationException("removeChild not supported!");
        }
    
        public Component getChild(int index){
            throw new UnsupportedOperationException("getChild not supported!");
        }
    }

    B.树枝节点,抽象组件实现

    package com.wf.composite.transparent;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @ClassName Composite
     * @Description 抽象组合,透明实现,实现的父类的空方法,树节点
     * @Author wf
     * @Date 2020/5/27 10:05
     * @Version 1.0
     */
    public class Composite  extends Component{
        List<Component> mComponents;
        public Composite(String name) {
            super(name);
            mComponents = new ArrayList<Component>();
        }
    
        @Override
        public String operation() {
            StringBuilder builder = new StringBuilder(this.name);
            for(Component component: this.mComponents){
                builder.append("
    ");
                builder.append(component.operation());
            }
            return builder.toString();
        }
        @Override
        public boolean addChild(Component component) {
            return this.mComponents.add(component);
        }
    
        @Override
        public boolean removeChild(Component component) {
            return this.mComponents.remove(component);
        }
    
        @Override
        public Component getChild(int index) {
            return this.mComponents.get(index);
        }
    }

    C.叶子节点

    package com.wf.composite.transparent;
    
    /**
     * @ClassName Leaf
     * @Description 组合对象,叶子节点
     * @Author wf
     * @Date 2020/5/27 10:12
     * @Version 1.0
     */
    public class Leaf extends Component {
    
        public Leaf(String name) {
            super(name);
        }
    
        @Override
        public String operation() {
            return this.name;
        }
    }

    D,测试代码

    package com.wf.composite.transparent;
    
    public class Test {
        public static void main(String[] args) {
            // 来一个根节点
            Component root = new Composite("root");
            // 来一个树枝节点
            Component branchA = new Composite("---branchA");
            Component branchB = new Composite("------branchB");
            // 来一个叶子节点
            Component leafA = new Leaf("------leafA");
            Component leafB = new Leaf("---------leafB");
            Component leafC = new Leaf("---leafC");
    
            root.addChild(branchA);
            root.addChild(leafC);
            branchA.addChild(leafA);
            branchA.addChild(branchB);
            branchB.addChild(leafB);
    
            String result = root.operation();
            System.out.println(result);
    
        }
    }

    测试结果如下:

    E.系统类图

    之所以,称为透明写法时,Component抽象组件中定义了3个空方法。

     2.安全写法

      由于透明写法中,抽象组件中定义几个空方法,作为公共组件,Leaf实现类,不需要使用到这几个空方法。

    所以,父类中提供的几个功能,并不是公共的。并不是子类中都能用到,所以,现在,去掉这几个方法。

      Composite实现中,如果需要自己进行扩展。

    A.顶层抽象组件

    package com.wf.composite.safe;
    
    /**
     * @ClassName Component
     * @Description 组合对象,顶层抽象组件,根节点
     * @Author wf
     * @Date 2020/5/27 10:00
     * @Version 1.0
     */
    public abstract class Component {
        protected String name;
    
        public Component(String name) {
            this.name = name;
        }
    
        public abstract String operation();
    }

    B.子节点实现

    package com.wf.composite.safe;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @ClassName Composite
     * @Description 抽象组合,透明实现,实现的父类的空方法,树节点
     * @Author wf
     * @Date 2020/5/27 10:05
     * @Version 1.0
     */
    public class Composite  extends Component {
        List<Component> mComponents;
        public Composite(String name) {
            super(name);
            mComponents = new ArrayList<Component>();
        }
    
        @Override
        public String operation() {
            StringBuilder builder = new StringBuilder(this.name);
            for(Component component: this.mComponents){
                builder.append("
    ");
                builder.append(component.operation());
            }
            return builder.toString();
        }
    
        public boolean addChild(Component component) {
            return this.mComponents.add(component);
        }
    
    
        public boolean removeChild(Component component) {
            return this.mComponents.remove(component);
        }
    
    
        public Component getChild(int index) {
            return this.mComponents.get(index);
        }
    }

    C.叶子节点实现

    package com.wf.composite.safe;
    
    /**
     * @ClassName Leaf
     * @Description 组合对象,叶子节点
     * @Author wf
     * @Date 2020/5/27 10:12
     * @Version 1.0
     */
    public class Leaf extends Component {
    
        public Leaf(String name) {
            super(name);
        }
    
        @Override
        public String operation() {
            return this.name;
        }
    }

    D.测试代码

    package com.wf.composite.safe;
    
    public class Test {
        public static void main(String[] args) {
            //由于Composite对抽象组件Component的功能,进行扩展
            //就不能使用多态的方式,调用子类的方法
    
            // 来一个根节点
            Composite root = new Composite("root");
            // 来一个树枝节点
            Composite branchA = new Composite("---branchA");
            Composite branchB = new Composite("------branchB");
            // 来一个叶子节点
            Component leafA = new Leaf("------leafA");
            Component leafB = new Leaf("---------leafB");
            Component leafC = new Leaf("---leafC");
    
            root.addChild(branchA);
            root.addChild(leafC);
            branchA.addChild(leafA);
            branchA.addChild(branchB);
            branchB.addChild(leafB);
    
            String result = root.operation();
            System.out.println(result);
    
        }
    }

    E.类图查看

     

    3.组合模式的具体实现示例

    【1】基于课程需求,透明实现

    A.系统需求分析

    假设希望输出课程目录信息,如下的结构。以此需求,利用组合模式设计系统,并开发。

    初步分析:

      上面的结构,有3层目录,可以设计3层嵌套结构。【但是,类太多,不推荐】

      可以巧妙用使用两层结构来处理3层目录。第一级目录,设计成课程包类【作为子节点】

      具体的课程作为叶子节点

      如:java设计模式,作为java架构师课程的叶子节点。

        而java入门课程,也得做为叶子节点。

        关于前后两级目录,使用参数进行优先级区分,如果优先级为1,表示第一层根目录。优先级为2,

    表示2级目录。

     B.测试代码

      这里以TDD的开发方式,先编写测试代码,再编写业务逻辑代码。

    具体的代码如下:

    package com.wf.composite.demo.transparent;
    
    public class Test {
        public static void main(String[] args) {
    
            System.out.println("============透明组合模式===========");
    
            CourseComponent javaBase = new Course("Java入门课程",8280);
            CourseComponent ai = new Course("人工智能",5000);
    
            CourseComponent packageCourse = new CoursePackage("Java架构师课程",2);
    
            CourseComponent design = new Course("Java设计模式",1500);
            CourseComponent source = new Course("源码分析",2000);
            CourseComponent softSkill = new Course("软技能",3000);
    
            packageCourse.addChild(design);
            packageCourse.addChild(source);
            packageCourse.addChild(softSkill);
    
            CourseComponent catalog = new CoursePackage("课程主目录",1);
            catalog.addChild(javaBase);
            catalog.addChild(ai);
            catalog.addChild(packageCourse);
    
            catalog.print();
    
    
        }
    }

    先编写测试代码,以编译报错,驱动编写业务代码。

    C.定义课程的抽象组件

    package com.wf.composite.demo.transparent;
    
    /**
     * @ClassName CourseComponent
     * @Description 课程抽象组件
     * @Author wf
     * @Date 2020/5/27 15:22
     * @Version 1.0
     */
    public abstract class CourseComponent {
    }

    为了解决报错,暂时得到一个空类。后面再进行代码填充。

    D.创建课程类【叶子节点】

    package com.wf.composite.demo.transparent;
    
    /**
     * @ClassName Course
     * @Description 具体组件实现叶子节点,课程类
     * @Author wf
     * @Date 2020/5/27 15:25
     * @Version 1.0
     */
    public class Course extends CourseComponent {
        private String name;
        private int price;
        public Course(String name, int price) {
            this.name = name;
            this.price = price;
        }
    }

    根据测试提示,需要新增两个成员,课程名称name,和购买价格price.

    其它代码,需要进一步填充。

    E.创建组件实现的叶子节点,课程归属类

    初步实现,会生成构造器,并手动添加成员变量。

    再来看,测试类中报错情况:

     显然,还有两个方法,未实现引起报错,解决报错,实现方法。

     根据提示,有两种实现方式,一种是定义抽象方法,一种是定义普通方法。

    结合组合模式透明写法,叶子节点不需要此方法,只能定义为普通方法。并且抽象类中方法不作实现

    这个方法是子节点中必须要实现的方法,所以在子节点类中手动实现。

    方法的功能是:

      添加1个或多个叶子节点,所以,是一对多的数据关系,定义一个List类型成员来存储多个节点。

    再来解决测试类中最后一个报错,print方法未定义的问题。由于这个方法是公用的,提取到父类中,作为抽象方法定义。

    抽象组件中定义抽象方法。抽象方法必须在子类中重写,否则子类引起报错。如下:

    package com.wf.composite.demo.transparent;
    
    /**
     * @ClassName Course
     * @Description 具体组件叶子节点,课程类
     * @Author wf
     * @Date 2020/5/27 15:25
     * @Version 1.0
     */
    public class Course extends CourseComponent {
        /** 课程名称*/
        private String name;
        private double price;
        public Course(String name, double price) {
            this.name = name;
            this.price = price;
        }
    
        @Override
        public void print() {
            System.out.println(name + " (¥" + price + "元)");
        }
    }

    子节点类,也需要重写抽象方法:

    package com.wf.composite.demo.transparent;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @ClassName CoursePackage
     * @Description 组合对象抽象组合,子节点,课程归属类
     * @Author wf
     * @Date 2020/5/27 15:32
     * @Version 1.0
     */
    public class CoursePackage extends CourseComponent {
        /** 课程归属名称*/
        private String name;
        /** 显示结构优先级*/
        private Integer level;
        private List<CourseComponent> items = new ArrayList<CourseComponent>();
    
        public CoursePackage(String name, Integer level) {
            this.name = name;
            this.level = level;
        }
    
        @Override
        public void addChild(CourseComponent catalogComponent) {
            items.add(catalogComponent);
        }
    
        @Override
        public void print() {
            //需要根据需求设置指定的输出格式
            System.out.println(this.name);
    
            for(CourseComponent catalogComponent : items){
                //控制显示格式
                if(this.level != null){
                    for(int  i = 0; i < this.level; i ++){
                        //打印空格控制格式
                        System.out.print("  ");
                    }
                    for(int  i = 0; i < this.level; i ++){
                        //每一行开始打印一个+号
                        if(i == 0){ System.out.print("+"); }
                        System.out.print("-");
                    }
                }
                //打印标题
                catalogComponent.print();
            }
    
        }
    }

    F.抽象组件的完整代码

    package com.wf.composite.demo.transparent;
    
    /**
     * @ClassName CourseComponent
     * @Description 课程抽象组件
     * @Author wf
     * @Date 2020/5/27 15:22
     * @Version 1.0
     */
    public abstract class CourseComponent {
        public void addChild(CourseComponent catalogComponent) {
            throw new UnsupportedOperationException("不支持添加操作");
        }
    
        public abstract void print();
    }

    G.运行测试  

      整个系统开发完成,进行最后的测试验证。输出结果如下:

    H.系统类图

     【2】基于系统文件目录需求,安全实现

    A.需求分析

     展示结果分析:

      上面系统文件目录,存在4级展示目录。还是可以基于展示层次优先级作区分。

    B.编写测试代码

    具体的测试代码如下:

    package com.wf.composite.demo.safe;
    
    
    public class Test {
        public static void main(String[] args) {
    
            System.out.println("============安全组合模式===========");
    
            File qq = new File("QQ.exe");
            File wx = new File("微信.exe");
    
            Folder office = new Folder("办公软件",2);
    
            File word = new File("Word.exe");
            File ppt = new File("PowerPoint.exe");
            File excel = new File("Excel.exe");
    
            office.add(word);
            office.add(ppt);
            office.add(excel);
    
            Folder wps = new Folder("金山软件",3);
            wps.add(new File("WPS.exe"));
            office.add(wps);
    
            Folder root = new Folder("根目录",1);
            root.add(qq);
            root.add(wx);
            root.add(office);
    
            System.out.println("----------show()方法效果-----------");
            root.show();
    
            System.out.println("----------list()方法效果-----------");
            root.list();
    
    
        }
    }

    注意:由于这里是基于组合模式的安全写法实现,不能使用多态,没有出现顶层抽象组件依赖。 

    C.顶层抽象组件定义

      基于TDD开发,根据测试代码中报错提示,驱动开发业务代码。

    package com.wf.composite.demo.safe;
    
    /**
     * 顶层抽象组件,目录类
     */
    public abstract class Directory {
    
        protected String name;
    
        public Directory(String name) {
            this.name = name;
        }
    
        public abstract void show();
    
    }

    D.定义叶子节点File文件类

    package com.wf.composite.demo.safe;
    
    /**
     * @ClassName File
     * @Description 叶子节点,文件类
     * @Author wf
     * @Date 2020/5/27 16:36
     * @Version 1.0
     */
    public class File extends Directory {
        //private String name;  //由于是公共成员,提取到父类中,声明为protected
        public File(String name) {
            super(name);
        }
    
        @Override
        public void show() {
            System.out.println(this.name);
        }
    }

    需要手动实现抽象组件,并重写方法。只需要打印名称即可。

    E.定义子节点文件夹Folder类

    package com.wf.composite.demo.safe;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @ClassName Folder
     * @Description 组合对象抽象组件,子节点
     * @Author wf
     * @Date 2020/5/27 16:41
     * @Version 1.0
     */
    public class Folder extends Directory {
        private Integer level;
        private List<Directory> dirs = new ArrayList<Directory>();
    
        public Folder(String name, Integer level) {
            super(name);
            this.name = name;
            this.level = level;
        }
    
        public void add(Directory dir) {
            dirs.add(dir);
        }
    
        @Override
        public void show() {
            //抽象方法,必须重写,设置输出格式
            System.out.println(this.name);
            for (Directory dir : this.dirs) {
                //控制显示格式
                if(this.level != null){
                    for(int  i = 0; i < this.level; i ++){
                        //打印空格控制格式
                        System.out.print("  ");
                    }
                    for(int  i = 0; i < this.level; i ++){
                        //每一行开始打印一个+号
                        if(i == 0){ System.out.print("+"); }
                        System.out.print("-");
                    }
                }
                //打印名称
                dir.show();
            }
    
        }
    
        /**
         * 扩展方法,不能使用多态调用
         */
        public void list() {
            for (Directory dir : this.dirs) {
                System.out.println(dir.name);
            }
        }
    }

    F.运行测试

     G.系统类图查看

    7.2.3.组合模式在源码中的应用

    7.2.3.1.jdk中hashMap应用组合模式
     public void putAll(Map<? extends K, ? extends V> m) {
            putMapEntries(m, true);
        }

    说明:

      putAll方法,传参抽象组件Map类型。就是组合模式的应用。

    7.2.3.2.jdk中ArrayList中addAll方法
    public boolean addAll(Collection<? extends E> c) {
            Object[] a = c.toArray();
            int numNew = a.length;
            ensureCapacityInternal(size + numNew);  // Increments modCount
            System.arraycopy(a, 0, elementData, size, numNew);
            size += numNew;
            return numNew != 0;
        }
     说明:

      addAll,传参Collection,组合模式应用。

    7.2.4.组合模式的问题总结

    1.透明写法与安全写法的区别?

    透明写法

      顶层抽象组件会定义所有的方法,包括叶子节点不需要的方法。只是作空方法声明。

    对于叶子节点,很容易错误调用,所以,违背最少知道原则。

    安全写法

      顶层抽象组件,只定义公共方法,调用时就不能使用多态的方式,只能子类自己new自己。

      避免叶子节点类实例,错误调用。

    组合模式一定是树形结构吗?

      答:不一定。只是强调整体部分的关系。它是针对整体与部分,寻找共同点,进行公共逻辑抽取的思维方式。

    7.2.4.1.组合模式的优缺点

    优点:

      1.清楚地定义层次分明的复杂对象,表示对象的全部或部分层次

      2.让客户端忽略层次的差异,方便对整个层次结构进行控制

      3.简化客户端代码

      4.符合开闭原则

    缺点:

      1.限制类型时会较为复杂

      2.使设计变得更加复杂

    附录:

      tom老师的刷票软件:https://github.com/gupaoedu-tom/3party-tools/tree/master/py12306

  • 相关阅读:
    使用正则表达式,取得点击次数,函数抽离
    爬取校园新闻首页的新闻
    网络爬虫基础练习
    综合练习:词频统计
    【Art】虹吸原理(5月份订正版)
    【失踪人口】准初三的JZSC
    【Mood】出大问题(最近很喜欢说这句话)
    【Matrix】矩阵加法&乘法
    【C++】stdio.h和cstdio
    【Theorem】中国剩余定理
  • 原文地址:https://www.cnblogs.com/wfdespace/p/12957670.html
Copyright © 2011-2022 走看看