精华内容
下载资源
问答
  • 2022-02-26 15:27:50

    【本专栏的代码都放在:Java中常用的几种设计模式

    在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。

    优点

    1. 封装不变部分,扩展可变部分。
    2. 提取公共代码,便于维护。
    3. 行为由父类控制,子类实现。

    缺点

    1. 每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。

    使用场景

    1. 有多个子类共有的方法,且逻辑相同。
    2. 重要的、复杂的方法,可以考虑作为模板方法。

    一、实现方式

    假设一个场景,我们在玩游戏的时候,都需要初始化加载游戏,然后开始游戏,最后结束游戏,这像是一套模板一样的操作,但是具体的实现方法却不同,例如:LOL 游戏与 CF 游戏的加载资源肯定不同,游戏的内容也不同。

    • 1、游戏抽象类
    package com.asurplus.common.template;
    
    /**
     * 游戏抽象类
     */
    public abstract class Game {
    
        // 初始化游戏
        abstract void init();
    
        // 开始游戏
        abstract void start();
    
        // 结束游戏
        abstract void end();
    
        // 模板
        public final void play() {
    
            // 初始化游戏
            init();
    
            // 开始游戏
            start();
    
            // 结束游戏
            end();
        }
    }
    
    • 2、LOL游戏类
    package com.asurplus.common.template;
    
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * LOL游戏
     */
    @Slf4j
    public class LolGame extends Game {
    
        @Override
        void init() {
            log.info("初始化 LOL 游戏");
        }
    
        @Override
        void start() {
            log.info("LOL 游戏开始");
        }
    
        @Override
        void end() {
            log.info("LOL 游戏结束");
        }
    }
    
    • 3、CF游戏类
    package com.asurplus.common.template;
    
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * CF游戏
     */
    @Slf4j
    public class CfGame extends Game {
    
        @Override
        void init() {
            log.info("初始化 CF 游戏");
        }
    
        @Override
        void start() {
            log.info("CF 游戏开始");
        }
    
        @Override
        void end() {
            log.info("CF 游戏结束");
        }
    }
    

    二、测试

    package com.asurplus.common.template;
    
    
    /**
     * 模板模式
     */
    public class TestMain {
    
        public static void main(String[] args) {
            // LOL 游戏
            Game lolGame = new LolGame();
            lolGame.play();
            System.out.println();
            // CF 游戏
            Game cfGame = new CfGame();
            cfGame.play();
        }
    
    }
    

    输出结果
    在这里插入图片描述
    可以看出,一套模板,可以有多个实现方式,从而实现了我们的工厂模式。

    如您在阅读中发现不足,欢迎留言!!!

    更多相关内容
  • 本书浅显易懂的介绍了JAVA线程相关的设计模式,通过程序范例和UML图示来一一解说,书中代码的重要部分加了标注以使读者更加容易理解,再加上图文并茂,对于初学者还是程序设计高手来说,这都是一本学习和认识JAVA...
  • Java 设计模式——组合模式

    万次阅读 多人点赞 2016-05-17 17:39:45
    分类 >> 设计模式 目录 文章目录 概述 版权说明 目录 @[toc] 组合模式 定义 情境分析 一般组合模式 模式分析 逻辑实现 模式小结 组合与迭代 深搜的试水之行 迭代的逆转 Ref GitHub 源码下载 征集 组合模式...

    概述

    有时我们可能会被要求处理一个层级结构明显的对象,比如上下级的公司员工、比如层级嵌套的文件夹,还有丰富多彩的美食菜单。可是,我们可能要屡试不爽地编写深度搜索代码、要小心翼翼地编写递归逻辑。现在你可以忘掉这些,学习一些新的技能,让你秒刷副本。当然,这句有些夸张,你可以忽略。只是它单纯地表达我对本文要说的这个模式的喜欢(也有可能只是因为我工作中恰好遇到这个问题)。


    版权说明

    著作权归作者所有。
    商业转载请联系作者获得授权,非商业转载请注明出处。
    本文作者:Q-WHai
    发表日期: 2016年5月17日
    本文链接:https://qwhai.blog.csdn.net/article/details/51437883
    来源:CSDN
    更多内容:分类 >> 设计模式


    目录

    组合模式

    定义

    将对象组合成树形结构以表示**“部分-整体”**的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

    情境分析

    看到组合的模式的定义,或许你就知道了这里的组合与我们平常讨论的*“继承-组合”*并不是同一个概念。因为在学习组合模式之前,我就是把这两个弄得有点混淆,所以一直没有认真地学习过它,以为组合也就是那么回事了嘛。可是,当我开始真的学习它的时候,才知道,这是两回事。
    定义上说的是部分与整体的层次结构,可能就这前半句还不能说明什么,不过我们可以从后半句中找到突破点,那就是单个对象与组合对象。也就是在组合对象中组合了一个部分的集合,这个集合中的元素也就是单个元素所具有的对象类型。
    当然,你不要理解成组合模式里也可以只有这个集合了。如果是这样,那么一个 List 完全可以可以搞定,又何必搞出个模式来突显逼格呢?
    现在我们就来“举个栗子”,对,是栗子。哈哈~
    我现在是一家中式餐厅的老板了。在我的店里你可以享用早餐、中餐、晚餐。早餐就简单点,只提供包子、油条跟豆浆吧(是的,这个不是主业)。午餐会丰盛一些,在午餐里你们选择北方菜或是南方菜。北方菜主要有:锅包肉、猪肉炖粉条、辣子鸡、干炸里脊、糖醋里脊、板栗烧鸡、地三鲜、红烧肉、回锅肉、 口水鸡、宫保鸡丁、可乐鸡翅;南方菜主要有:炒米粉、南瓜饼、南焖羊肉、蒸排骨、肉片炒青椒、水果皮萨、北炒鱼香茄子、糯米糍团、芥菜煲。晚餐上可以吃得精致一些,主要有地道小吃和甜点。地道小吃:肉夹馍、羊肉泡馍、乌冬面、章鱼小丸子、葱油饼、老婆饼;甜点:冰淇淋、鲜奶蛋糕还有蜜汁藕。
    把上面的描述转换成图片菜单就像下面这样的:

    这里写图片描述

    当然,这里并不够齐全,只是为了照顾图片的大小(当然也不可否认是博主偷懒了),只绘制了其中的一部分,不过足够我们说明问题了。

    一般组合模式

    模式分析

    在一般组合模式里,我们只做了一件事,那就是模糊了简单元素与复杂元素。怎么说明这一点呢?针对上面举的例子来说,我们的每一种菜都是一个简单元素,而每一种菜系(南方/北方/小说/甜点)或是餐饮的类型(早餐/午餐/晚餐)都是一个复杂元素,因为这里又包含若干的简单元素。我们把菜系定义为 Menu,而每一道具体的菜则定义成 MenuItem。这样我们就可以绘制出一般组合模式的类图,如下:

    这里写图片描述

    这里面我们的菜单(Menu)和菜单项(MenuItem)都继承自一个 MenuComponent。MenuComponent 是一个抽象的类。当 Menu 和 MenuItem 继承自同一个类时,我们就可以实现模糊简单元素与复杂元素了,因为我们可以按照处理 MenuComponent 的方式处理 Menu 和 MenuItem。这里我们只是模糊了这两个的分界,却不能真的等同看待。很简单,上面的的 MenuItem 最起码有一个价格的属性、而 Menu 就不存在这个属性;Menu 可以有一个 add MenuComponent 的方法,而 MenuItem 则不可能会有。
    说到这里,可能你会说组合模式并不完美。是的,我也这么觉得。它让这件事情模糊了,让 MenuComponent 的使用产生了歧义。比如我们在使用它的时候,根本不知道它的某一个方法是可以正常使用。比如,一个 MenuComponent(可实际的类型可能是 Menu,而我们不知道),这时可能会调用它的 getPrice() 方法,这是有问题的,逻辑上是走不通的。那么,我们就必须要为 Menu 的这个方法抛出一个异常(异常是一个心机 boy,我们都不太喜欢它)。当然,也可以在外部使用 instanceof 关键字处理。可是,这样的处理总让我有一种非面向对象的处理过程,所以还是抛出异常吧。
    另外,Menu 中组合了 MenuItem,从这一点来看,倒是有几分“继承-组合”的意味。

    逻辑实现

    先来看看抽象类,这个是基础:
    MenuComponent.java

    public abstract class MenuComponent {
    
        public String getName() {
            throw new UnsupportedOperationException("暂不支持此操作");
        }
    
        public String getDescription() {
            throw new UnsupportedOperationException("暂不支持此操作");
        }
    
        public double getPrice() {
            throw new UnsupportedOperationException("暂不支持此操作");
        }
    
        public boolean isVegetarian() {
            throw new UnsupportedOperationException("暂不支持此操作");
        }
    
        public void print() {
            throw new UnsupportedOperationException("暂不支持此操作");
        }
    
        public void add(MenuComponent menuComponent) {
            throw new UnsupportedOperationException("暂不支持此操作");
        }
    
        public void remove(MenuComponent menuComponent) {
            throw new UnsupportedOperationException("暂不支持此操作");
        }
    
        public MenuComponent getChild(int childIndex) {
            throw new UnsupportedOperationException("暂不支持此操作");
        }
    }
    

    而在 Menu 的具体类中,虽然是继承了 MenuComponent,可是它的抽象方法又不能全部重写。原因上面也说了,这里不赘述了。可是,由于 Java 语法的客观存在,所以这里我们抛出了一个异常。
    Menu.java

    public class Menu extends MenuComponent {
    
        private String name = null;
        private String desc = null;
        private List<MenuComponent> menuComponents = null;
        
        public Menu(String _name, String _desc) {
            name = _name;
            desc = _desc;
        }
        
        @Override
        public String getName() {
            return name;
        }
    
        @Override
        public String getDescription() {
            return desc;
        }
    
        @Override
        public void print() {
            System.out.println("\nMenu: { " + name + ", " + desc + " }");
            if (menuComponents == null) {
                return;
            }
            System.out.println("-------------------------");
            
            for (MenuComponent menuComponent : menuComponents) {
                menuComponent.print();
            }
        }
    
        @Override
        public void add(MenuComponent menuComponent) {
            if (menuComponents == null) {
                menuComponents = new ArrayList<MenuComponent>();
            }
            menuComponents.add(menuComponent);
        }
    
        @Override
        public MenuComponent getChild(int childIndex) {
            if (menuComponents == null || menuComponents.size() <= childIndex) {
                return null;
            }
            return menuComponents.get(childIndex);
        }
    }
    

    基于上面对 Menu 类的说明,这里的 MenuItem 类的实现过程也是一样:只重写能够重写的部分,不能重写的地方抛出一个异常等待上层处理。
    MenuItem.java

    public class MenuItem extends MenuComponent {
    
        private String name = null;
        private String desc = null;
        private boolean vegetarian = false;
        private double price = 0.0d;
        
        public MenuItem(String _name, String _desc, boolean _vegetarian, double _price) {
            this.name = _name;
            this.desc = _desc;
            this.vegetarian = _vegetarian;
            this.price = _price;
        }
        
        @Override
        public String getName() {
            return name;
        }
    
        @Override
        public String getDescription() {
            return desc;
        }
    
        @Override
        public double getPrice() {
            return price;
        }
    
        @Override
        public boolean isVegetarian() {
            return vegetarian;
        }
    
        @Override
        public void print() {
            System.out.println("MenuItem: { " + name + ", " + desc + ", " + vegetarian + ", " + price + " }");
        }
    }
    

    上面的代码是整个一般组合模式的关键部分,这是需要注意的是它们的 print() 方法。对于 MenuItem 的 print() 来说,是很常规的打印,而 Menu 的打印则需要作处理。因为我们正常的理解里一个菜单因为会包含很多菜单项,所以,这里我们就把当前菜单下的所以菜单打印一遍。不过,这也不是什么难事,因为在每份 Menu 中都有一个 MenuItem 的列表。好了,问题解决。详情参见上面的代码部分。

    模式小结

    从上面的例子也可以看出,组合模式在解决有层级关系时,有着得天独厚的优势。思路清晰、代码优雅。唯一的不足是我们要针对不同的情况抛出相应的异常。

    组合与迭代

    对于组合模式息息相关的另一种模式——迭代模式,它在组合模式中可以说有着重要的地位。在上面的代码中,有点编程逻辑的人应该都可以发现,它们的 print() 方法是对象内部的操作。也就是说,如果我想要通过一个 Menu 操作一个 MenuItem 就必须在 Menu 内部进行实现。这是不现实的,因为需求变化的速度,可能隔了几秒连它的亲妈也不认识了。正因为如此,所以我们就必须想办法从外部拿到 Menu 中的 MenuItem。
    比如现在我想知道餐厅里所有的素食有哪些,如果我们不去改动原有代码,那么就可以添加一个外部的迭代逻辑。

    深搜的试水之行

    或许你又会说,这里根本不需要使用迭代,用一次深搜就 OK 了。是的没错,而且对于一个数据结构基本功还可以的同学,可以马上写出一个深搜的解决方案。这里给出我的深搜方案:

    public void showVegetarMenu(MenuComponent menu) {
            List<MenuComponent> visited = new ArrayList<>();
            showVegetarMenu(menu, visited);
        }
        
        private void showVegetarMenu(MenuComponent menu, List<MenuComponent> visited) {
            if (visited.contains(menu)) {
                return;
            }
            
            if (menu instanceof MenuItem) {
                if (menu.isVegetarian()) {
                    System.out.println(menu);
                }
                return;
            }
            
            List<MenuComponent> children = ((Menu) menu).getChildren();
            for (int i = 0; i < children.size(); i++) {
                showVegetarMenu(children.get(i), visited);
            }
        }
    
    素食菜单(深搜)
    MenuItem: { 包子, bun, true, 1.5 }
    MenuItem: { 油条, fritters, true, 1.2 }
    MenuItem: { 豆浆, milk, true, 2.0 }
    MenuItem: { 炒米粉, Fried noodles, true, 8.0 }
    MenuItem: { 冰淇淋, ice cream, true, 5.0 }
    

    结果不出意外。能够使用深搜,已然是逼格满满了。不过,使用深搜让我有一种面向过程编程的感觉,不够优雅。下面就让我用迭代器来实现一次华丽的逆转吧。

    迭代的逆转

    首先我们为 MenuComponent 添加一个 createIterator() 方法。就像下面这样:
    MenuComponent.java

    public abstract class MenuComponent {
    
        ( ... 省略重复的 N 条  ... )
        
        public abstract Iterator<MenuComponent> createIterator();
    }
    

    由于这里添加的是一个抽象的方法,那么在 Menu 和 MenuItem 中就要必须重写这个 createIterator() 方法。
    Menu.java

    public class Menu extends MenuComponent {
    
        private CompositeIterator iterator = null;
        
        ( ... 省略重复的 N 条  ... )
    
        @Override
        public Iterator<MenuComponent> createIterator() {
            if (iterator == null) {
                iterator = new CompositeIterator(menuComponents.iterator());
            }
            
            return iterator;
        }
    }
    

    MenuItem.java

    public class MenuItem extends MenuComponent {
    
        ( ... 省略重复的 N 条  ... )
    
        @Override
        public Iterator<MenuComponent> createIterator() {
            return new NullIterator();
        }
    }
    

    在上面两段代码中提到了两个迭代器类:CompositeIterator、NullIterator。这里有参照书本上的逻辑,不过也有改动,因为书本的迭代器没有通用性,下面会对这一点进行说明的。
    CompositeIterator.java

    public class CompositeIterator implements Iterator<MenuComponent> {
    
        private Stack<Iterator> stack = new Stack<>();
        
        public CompositeIterator(Iterator iterator) {
            stack.push(iterator);
        }
        
        @Override
        public boolean hasNext() {
            if (stack.empty()) {
                return false;
            }
            
            Iterator iterator = stack.peek();
            if (!iterator.hasNext()) {
                stack.pop();
                return hasNext();
            }
            
            return true;
        }
    
        @Override
        public MenuComponent next() {
            if (hasNext()) {
                Iterator iterator = stack.peek();
                MenuComponent component = (MenuComponent) iterator.next();
                if (component instanceof Menu) {
                    Iterator menuIterator = component.createIterator();
                    if (!stack.contains(menuIterator)) {
                        stack.push(menuIterator);
                    }
                }
                return component;
            }
            
            return null;
        }
    
        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }
    

    这里的栈结构使用得很巧妙,因为这个栈的使用让我想到在 LeetCode 上的一道算法题,也是使用栈来实现,而且比一般的算法复杂度低很多,如果我不犯懒的话,应该会写那一篇博客的。咳咳,扯远了,回到正题。有关于栈的使用是一些数据结构和 Java api 的基础,这里不多说什么了。还有这里的 hasNext() 和 next() 方法,这里要求你对数据结构和 Java api(主要是 Stack 这一块)比较熟悉。所以,如果你看到这个地方有什么不太理解的,可以留言,也可以自行复习一下这两块内容。

    NullIterator.java

    public class NullIterator implements Iterator<MenuComponent> {
    
        @Override
        public boolean hasNext() {
            return false;
        }
    
        @Override
        public MenuComponent next() {
            return null;
        }
    
        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }
    

    因为每个菜单项都不可能什么子菜单项,也就不存在什么迭代器了,所以在 MenuItem 中就可以返回一个 Null 的迭代器。当然,这是理想的做法。你也可以直接返回 null,只是这样一来,在上层就要多一次判空处理,相比较而言,这样的实现更优雅。
    程序的结果自然不出所料:

    素食菜单(迭代)
    MenuItem: { 包子, bun, true, 1.5 }
    MenuItem: { 油条, fritters, true, 1.2 }
    MenuItem: { 豆浆, milk, true, 2.0 }
    MenuItem: { 炒米粉, Fried noodles, true, 8.0 }
    MenuItem: { 冰淇淋, ice cream, true, 5.0 }
    

    只是,如果你只采用书本上的迭代器来实现,就会出现多级菜单下的菜单项被 show 了 N 遍。而你只能一脸懵逼。


    Ref

    • 《Head First 设计模式》

    GitHub 源码下载


    征集

    如果你也需要使用ProcessOn这款在线绘图工具,可以使用如下邀请链接进行注册:
    https://www.processon.com/i/56205c2ee4b0f6ed10838a6d

    展开全文
  • Java设计模式

    万次阅读 多人点赞 2019-09-03 23:20:31
    Java设计模式 1.工厂模式 工厂模式一般分为简单工厂、工厂、抽象工厂3钟情况,属于创建型设计模式。 2.生成器模式 3.观察者模式 4.桥接模式 5.代理模式 6.状态模式 7.访问者模式 ...

    Java设计模式

    1.工厂模式

    2.生成器模式

    3.观察者模式

    4.桥接模式

    5.代理模式

    6.状态模式

    7.访问者模式

    8.命令模式

    9.装饰器模式

    10.组合模式

    1.工厂模式

    工厂模式一般分为简单工厂、工厂、抽象工厂3种情况,属于创建型设计模式。

    简单工厂(静态工厂)

    //定义小汽车接口:ICar.java
    public interface ICar{
      //由于工厂模式仅关系对象的创建,为说明方便,无需定义方法
    }
    
    //下面定义高、中、低档具体的汽车
    //高档小汽车:TopCar.java
    public class TopCar implements ICar{
    }
    //中档小汽车:MidCar.java
    public class MidCar implements ICar {
    }
    //低档小汽车:LowCar.java
    public class LowCar implements ICar {
    }
    
    //简单工厂:CarSimpleFactory.java
    public class CarSimpleFactory {
        public static final String TOPTYPE = "toptype";
        public static final String MIDTYPE = "midtype";
        public static final String LOWTYPE = "lowtype";
        public static ICar create(String mark){
        ICar obj = null;
        if(mark.equals(TOPTYPE)){ //如果是高档类型
          obj = new TopCar();  //则创建高档车对象
        }
        else if(mark.equals(MIDTYPE)){
        obj = new MidCar();
      }
        else if(mark.equals(LOWTYPE)){
        obj = new LowCar();
      }
        return obj;    //返回选择的对象
      }
    }
    
    //测试程序:CarTest.java
    public class CarTest {
      public static void main(String[] args) {
      //从工厂中创建对象
        ICar obj = CarSimpleFactory.create("toptype");
      }
    }
    

    工厂

    //定义小汽车接口:ICar.java
    public interface ICar {
    //由于工厂模式仅关系对象的创建,为说明方便,无需定义方法
    }
    //定义高、中、低档具体的小汽车
    //高档小汽车:TopCar.java
    public class TopCar implements ICar {
    }
    //中档小汽车:MidCar.java
    public class MidCar implements ICar {
    }
    //低档小汽车:LowCar.java
    public class LowCar implements ICar {
    }
    //定义抽象工厂:AbstractFactory.java
    public abstract class AbstractFactory {
    public abstract ICar create();
    }
    //定义高档小汽车工厂:TopFactory.java
    public class TopFactory extends AbstractFactory {
    public ICar create() {
        return new TopCar(); //高档工厂生成高档小汽车对象
      }
    }
    //定义中档小汽车工厂:MidFactory.java
    public class MidFactory extends AbstractFactory {
    public ICar create() {
        return new MidCar(); //中档工厂生成中档小汽车对象
      }
    }
    //定义低档小汽车工厂:LowFactory.java
    public class LowFactory extends AbstractFactory {
    public ICar create() {
        return new LowCar(); //低档工厂生成低档小汽车对象
      }
    }
    //测试类:CarTest.java
    public class CarTest {
    public static void main(String []args){
        AbstractFactory obj = new TopFactory();//多态创建高档工厂
        ICar car = obj.create();    //获得高档工厂中的小汽车对象
      }
    }
    

    抽象工厂

    //小汽车接口
    public interface ICar { }
    public class TopCar implements ICar { }
    public class MidCar implements ICar { }
    public class LowCar implements ICar { }
    
    //定义公共汽车接口、高、中、低档公共汽车类
    public interface IBus { }
    public class UpBus implements IBus { }
    public class MidBus implements IBus { }
    public class DnBus implements IBus { }
    
    //定义抽象工厂:AbstractFactory.java
    public absttract class AbstractFactory {
    public abstract ICar createCar(); //产生小汽车对象
    public abstract IBus createBus(); //产生公共汽车对象
    }
    
    //定义高档工厂:TopFactory.java
    public class TopFactory extends AbstractFactory {
      public ICar createCar() {
        return new TopCar();  //高档工厂生成高档小汽车对象
      }
      public IBus createBus() {
        return new UpBus();  //高档工厂生成高档公共汽车对象
      }
    }
    
    //定义中档工厂:MidFactory.java
    public class MidFactory extends AbstractFactory {
      public ICar createCar() {
        return new MidCar();  //中档工厂生成中档小汽车对象
      }
      public IBus createBus() {
        return new MidBus();  //中档工厂生成中档公共汽车对象
      }
    }
    //定义低档工厂:LowFactory.java
    public class LowFactory extends AbstractFactory {
      public ICar createCar() {
        return new LowCar();  //低档工厂生成中档小汽车对象
      }
      public IBus createBus() {
        return new DnBus();  //低档工厂生成中档公共汽车对象
      }
    }
    

    2.生成器模式

    生成器模式也称为建造者模式。生成器模式的意图在于将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。在软件设计中,有时候面临着一个非常复杂的对象的创建工作。这个复杂的对象通常可以分成几个较小的部分,由各个子对象组合出这个复杂对象的过程相对来说比较稳定,但是子对象的创建过程各不相同并且可能面临变化。根据OOD中的OCP原则,应该对这些子对象的创建过程进行变化封装。

    生成器思路是产品类与创建产品的类相分离。产品类仅1个,创建产品的类有n个。

    生成器设计模式涉及4个关键角色:产品(Product)、抽象生成器(IBuild)、具体生成器(Builder)、指挥者(Director)。
    在这里插入图片描述

    (1)定义1个产品类。
    public class Unit1{……}
    public class Unit2{……}
    public class Unit3{……}
    public class Product {
    Unit1 u1;
    Unit2 u2;
    Unit3 u3;
    }
    由于不在该类完成Product类对象的创建,所以无需显示定义构造方法。
    
    (2)定义n个生成器Build类。
    根据语义,生成器是用来生成Product对象的,因此一般来说,Product是生成器类的一个成员变量;
    根据语义,每创建一个Product对象,本质上都需要先创建Unit1,Unit2,…, UnitN,再把它们组合成所需的Product对象,
    因此需要n个createUnit()方法及一个组合方法composite();由于createUnit()及composite()是共性,
    因此可定义共同的生成器类接口, n个生成器类均从此接口派生即可。代码如下。
    //定义生成器类接口IBuild
    public interface IBuild {
    public void createUnit1();
    public void createUnit2();
    public void createUnit3();
    public Product composite();    //返回值是Product对象
    }
    //定义3个生成器类
    public class BuildProduct implements IBuild { //生成第一种Product
    Product p = new Product();     //Product是成员变量
    public void createUnit1() {
    //p.u1= ...       //创建Unit1
    }
    public void createUnit2() {
    //p.u2 = ...       //创建Unit2
    }
    public void createUnit3() {
    //p.u3 =  //创建Unit3
    }
    public Product composite() {
    //...   //关联Unit,Unit2,Unit3
    return p;  //返回Product对象p
    }
    }
    public class BuildProduct2 implements IBuild {  //生成第2种Product
    Product p = new Product();//Product是成员变量
    public void createUnit() {/*p.u = ...... */} //创建Unit
    public void createUnit2(){/*p.u2 = ...... */} //创建Unit2
    public void createUnit3(){/*p.u3 = ...... */} //创建Unit3
    public Product composite() {
    //......    //关联Unit1,Unit2,Unit3
    return p;   //返回Product对象p
    }
    }
    public class BuildProduct3 implements IBuild {  //生成第3种Product
    Product p = new Product();//Product是成员变量
    public void createUnit1() {/*p.u1 = ...... */} //创建Unit1
    public void createUnit2(){/*p.u2 = ...... */} //创建Unit2
    public void createUnit3(){/*p.u3 = ...... */} //创建Unit3
    public Product composite() {
    //......  //关联Unit1,Unit2,Unit3
    return p;  //返回Product对象p
    }
    }
    通过上述代码可知,若需求分析发生变化,只需增加或删除相应的生成器类即可,无需修改已有的类代码。
    (3)定义1个统一调度类,也叫指挥者(Director)类,是对生成器接口IBuild的封装。该类及简单的测试代码如下。
    public class Director {
    private IBuild build;
    public Director(IBuild build){
    this.build = build;
    }
    public Product build(){
    build.createUnit1();
    build.createUnit2();
    build.createUnit3();
    return build.composite();
    }
    public static void main(String []args){
    IBuild build = new BuildProduct();
    Director direct = new Director(build);
    Product p = direct.build();
    }
    }
    

    3.观察者模式

    观察者设计模式适合解决多种对象跟踪一个对象数据变化的程序结构问题,有一个称作“主题”的对象和若干个称作“观察者”的对象。有一个主题数据——温度,3个观察者—温度日志、温度曲线、温度报警。因此观察者设计模式涉及两种角色:主题和观察者。
    观察者设计模式可以从以下递推中得出一些重要结论。
    ● 主题要知道有哪些观察者对其进行监测,因此主题类中一定有一个集合类成员变量,包含了观察者的对象集合。
    ● 既然包含了观察者的对象集合,那么,观察者一定是多态的,有共同的父类接口。
    ● 主题完成的主要功能是:可以添加观察者,可以撤销观察者,可以向观察者发消息,引起观察者响应。这三个功能是固定的,因此主题类可以从固定的接口派生。
    因此,编制观察者设计模式,要完成以下功能类的编制。
    ● 主题ISubject接口定义。
    ● 主题类编制。
    ● 观察者接口IObserver定义。
    ● 观察者类实现。
    在这里插入图片描述

    (1)观察者接口IObserver。
    
        public interface IObserver {
            public void refresh(String data);
        }
        
    (2)主题接口ISubject。
    
        public interface ISubject {
            public void register(IObserver obs);       //注册观察者
            public void unregister(IObserver obs);     //撤销观察者
            public void notifyObservers();             //通知所有观察者
        }
        
    (3)主题实现类Subject。
    
        public class Subject implements ISubject {
            private Vector<IObserver> vec = new Vector();  //观察者维护向量
            private String data;                           //主题中心数据
    
            public String getData() {
                return data;
            }
            public void setData(String data) {              //主题注册(添加)
                this.data = data;
            }
            public void register(IObserver obs) {           //主题注册(添加)观察者
             vec.add(obs);
            }
    
            public void unregister(IObserver obs) {         //主题撤销(删除)观察者
                if(vec.contains(obs))
                    vec.remove(obs);
            }
            public void notifyObservers(){             //主题通知所有观察者进行数据响应
                for(int i=0; i<vec.size(); i++){
                    IObserver obs = vec.get(i);
                    obs.refresh(data);
                }
            }
        }
        
        主题实现类Subject是观察者设计模式中最重要的一个类,包含了观察者对象的维护向量vec以及主题中心数据data变量与具体观察者对象的关联
        方法(通过nitofyObservers())。也就是说,从此类出发,可以更深刻地理解ISubject为什么定义了3个方法、IObserver接口为什么定义了1个方法。
        
    (4)一个具体观察者类Observer。
    
        public class Observer implements IObserver {
            public void refresh(String data) {
                System.out.println("I have received the data:" +data);
            }
        }
        
    (5)一个简单的测试类Test。
    
        public class Test {
            public static void main(String[] args) {
                IObserver obs = new Observer();    //定义观察者对象
                Subject subject = new Subject();
                //定义主题对象
                subject.register(obs);             //主题添加观察者
                subject.setData("hello");          //主题中心数据发生变动
                subject.notifyObservers();         //通知所有观察者进行数据响应
            }
        }
        
    该段代码的含义是:当主题中心数据变化(通过setData方法)后,主题类subject要调用notifyObservers()方法,
    通知所有观察者对象接收数据并进行数据响应。
    

    4.桥接模式

    桥接模式是关于怎样将抽象部分与它的实现部分分离,使它们都可以独立地变化的成熟模式。
    在这里插入图片描述

    (1)定义邮寄接口IPost。
    
        public interface IPost{   //邮局
            public void post();   //发送功能
        }
        
    (2)两个具体邮寄类SimplePost、MarkPost。
    
        //平信邮寄类SimplePost
        class SimplePost implements IPost{     //平信
            public void post(){                //发送
                System.out.println("This is Simple post");
            }
        }
        //挂号邮寄类
        class MarkPost implements IPost{       //挂号
            public void post(){                //发送
                System.out.println("This is Mark post");
            }
        }
        
      经过(1)、(2)的论述,完成了语义的前半部分定义:邮局有发送功能;发送有两种方式,平邮和挂号。
      
    (3)抽象事物类AbstractThing。
    
        abstract class AbstractThing{ //抽象事物
    
            private IPost obj;         //有抽象发送功能
            public AbstractThing(IPost obj){
                this.obj = obj;
            }
            public void post(){
                obj.post();
            }
        }
        
    该类是桥接模式的核心。分析语义“信件和包裹共享平邮与挂号功能”:信件、包裹是两个不同的事物,它们有共享的功能,
    也一定有相异的功能。共享的功能一定能封装到一个类中,又由于该类不能代表一个具体的事物,所以把它定义成abstract类是恰当的。
    该类共享的是多态成员obj,是IPost类型的,是抽象的、泛指的,用一条语句表明了事物共享平邮和发送功能。
    
    (4)具体事物类Letter、Parcel。
    
        //信件类Letter
        class Letter extends AbstractThing{
            public Letter(IPost obj){
                super(obj);
            }
            //其他独有变量和方法
        }
        //包裹类Parcel
        class Parcel extends AbstractThing{
            public Parcel(IPost obj){
                super(obj);
            }
            //其他独有变量和方法
        }
        
        //编制一个简单的测试类
        public class Test {
            public static void main(String[] args) {
                IPost p = new SimplePost();
    
                Letter letter = new Letter(p);
                letter.post();
            }
        }
        
    第一种情况:若增加了新的事物,则只需从Abstract派生一个类即可,其他无需改变。
    
        class NewThing extends AbstractThing{
            public NewThing(IPost obj){
                super(obj);
            }
            //其他独有变量和方法
        }
        
    第二种情况:若增加了新的邮寄类别,比如特快专递,则只需从IPost接口派生一个类即可,其他无需改变。
    
        class EMSPost implements IPost{        //特快专递
        public void post(){                //发送
                System.out.println("This is EMS post");
            }
        }
    

    5.代理模式

    代理对象可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户不能看到的内容和服务或者添加客户需要的额外服务。代理模式则是一种可以很好实现客户对象与代理对象分离的策略。

    代理模式的定义如下:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式的英文叫作Proxy或Surrogate,它是一种对象结构型模式。其抽象UML图如图所示
    在这里插入图片描述
    代理模式包含如下角色
    ● ISubject:抽象主题角色,是一个接口。该接口是对象和它的代理共用的接口。
    ● RealSubject:真实主题角色,是实现抽象主题接口的类。
    ● Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。代理对象提供与真实对象相同的接口,以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。以买电视为例,其代码如下。
    (1)定义抽象主题——买电视。

    interface ITV{
            public void buyTV();
        }
    

    (2)定义实际主题——买电视过程。

    class Buyer implements ITV{
        public void buyTV(){
            System.out.println("I have bought the TV by buyer proxy");
        }
    }
    

    真正的付费是由购买者完成的。
    (3)定义代理。

    class BuyerProxy implements ITV{
        private Buyer buyer;
        public BuyerProxy(Buyer buyer){
            this.buyer = buyer;
        }
        public void buyTV(){
            preProcess();
            buyer.buyTV();
            postProcess();
        }
        public void preProcess(){
            //询问客户需要电视类型、价位等信息
        }
        public void postProcess(){
            //负责把电视送到客户家
        }
    }
    

    电视代理商BuyerProxy与购买者Buyer都实现了相同的接口ITV,是对Buyer对象的进一步封装。着重理解buyTV()方法:首先代理商要通过preProcess()询问客户买电视的类型、价位等信息,然后购买者通过buyer.buyTV()自己付费完成电视购买,最后代理商通过postProcess()协商具体的送货服务、产品三包等。

    代理模式最突出的特点是:代理角色与实际主题角色有相同的父类接口。常用的代理方式有4类:虚拟代理、远程代理、计数代理、动态代理

    虚拟代理
    虚拟代理的关键思想是:如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。当用户请求一个“大”对象时,虚拟代理在该对象真正被创建出来之前扮演着替身的角色;当该对象被创建出来之后,虚拟代理就将用户的请求直接委托给该对象。

    远程代理
    远程代理的含义是:为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以在同一台主机中,也可在另一台主机中。也就是说,远程对象驻留于服务器上,客户机请求调用远程对象调用相应方法,执行完毕后,结果由服务器返回给客户端。
    在这里插入图片描述
    计数代理
    当客户程序需要在调用服务提供者对象的方法之前或之后执行日志或计数的额外功能时,就可以使用计数代理模式。计数代理模式并不是把这些额外操作的代码直接添加到源服务中,而是把它们封装成一个单独的对象,这就是计数代理。

    动态代理
    对代理模式而言,一个主题类与一个代理类一一对应,这也是静态代理模式的特点。
    静态代理模式简图
    但是,也常存在这样的情况,有n个主题类,但是代理类中的“前处理、后处理”都是一样的,仅调用主题不同,需要编制如图所示的程序框架。
    [动态代理模式简图]
    也就是说,多个主题类对应一个代理类,共享“前处理、后处理”功能,动态调用所需主题,大大减小了程序规模,这就是动态代理模式的特点。实现动态代理的关键技术是反射。

    6.状态模式

    状态模式为研究各种状态以及状态间相互转化的实现方式提出了一种较好的设计思路。

    ● 状态类有共同的父接口(或抽象类), n个不同的状态实现类。
    ● 事物类中包含状态类父接口成员变量声明,用以反映语义“事物有n个状态”。
    ● 事物类中一定有方法选择分支,判断事物当前处于何种状态。

    状态模式必须完成如下内容的编制:
    ● State:状态接口,封装特定状态所对应的行为。
    ● ConcreteState:具体实现状态处理的类。
    ● Context:事物类,也称上下文类,通常用来定义多态状态接口,同时维护一个用来具体处理当前状态的示例对象。

    状态模式的UML抽象类图如图所示
    在这里插入图片描述
    状态模式的具体抽象代码如下。
    (1)定义状态抽象接口IState。

    interface IState{
        public void goState();
    }
    

    (2)定义状态实现类。

    class ConcreteStateA implements IState{//定义状态A类
        public void goState(){
            System.out.println("This is ConcreteStateA");
        }
    }
    class ConcreteStateB implements IState{//定义状态B类
        public void goState(){
            System.out.println("This is ConcreteStateB");
        }
    }
    

    (3)定义状态上下文维护类Context。

    class Context{            //上下文有n种状态
        private IState state;
        public void setState(IState state){
            this.state = state;
        }
        public void manage(){
            //此处代码根据条件选择某种状态
            state.goState(); //执行某种状态功能
        }
    }
    

    Context类是实现状态模式的关键,本部分仅列出了状态模式的基本代码

    7.访问者模式

    访问者模式的目的是封装一些施加于某种数据结构元素之上的操作,一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。为不同类型的元素提供多种访问操作方式,且可以在不修改原有系统的情况下增加新的操作方式,这就是访问者模式的模式动机。

    访问者模式抽象类图
    在这里插入图片描述
    访问者模式主要涉及以下四种角色:
    ● IElement:抽象的事物元素功能接口,定义了固定功能方法及可变功能方法接口。
    ● Element:具体功能的实现类。
    ● IVisitor:访问者接口,为所有访问者对象声明一个visit方法,用来代表为对象结构添加的功能,原则上可以代表任意的功能。
    ● Visitor:具体访问者实现类,实现要真正被添加到对象结构中的功能。

    考虑这样一个应用:已知三点坐标,编写功能类,求该三角形的面积和周长。

    如果采用访问者模式,应当这样思考:目前已确定的需求分析是求面积和周长功能,但有可能将来求三角形的重心、垂心坐标,内切、外界圆的半径等,因此,在设计时必须考虑如何屏蔽这些不确定情况。具体代码如下。

    1.定义抽象需求分析接口IShape

    interface IShape{
        float getArea();             //明确的需求分析
        float getLength();           //明确的需求分析
        Object accept(IVisitor v);   //可扩展的需求分析
    }
    

    着重理解可扩展的需求分析方法accept(),它在形式上仅是一个方法,但是按访问者模式而言,它却可以表示将来可以求重心、垂心坐标等功能,是一对多的关系,因此IVisitor一般来说是接口或抽象类,“多”项功能一定是由IVisitor的子类来实现的。那么为什么返回值是Object类型呢?可以这样理解,例如重心坐标由两个浮点数表示,外接圆半径由一个浮点数表示,为了屏蔽返回值差异,返回值定义成Object,表明可以返回任意对象类型。

    2.定义具体功能实现类Triangle

    class Triangle implements IShape{
        float x1, y1, x2, y2, x3, y3;           //三角形三点坐标
        public Triangle(float x1, float y1, float x2, float y2, float x3, float y3){
    
            this.x1=x1; this.y1=y1;
            this.x2=x2; this.y2=y2;
            this.x3=x3; this.y3=y3;
        }
        public float getDist(float u1, float v1, float u2, float v2){   //求任意两点距离
            return (float)Math.sqrt((u1-u2)*(u1-u2)+(v1-v2)*(v1-v2));
        }
        public float getArea(){            //固定需求分析求面积
            float a = getDist(x1, y1, x2, y2);
            float b = getDist(x1, y1, x3, y3);
            float c = getDist(x2, y2, x3, y3);
            float s = (a+b+c)/2;
            return (float)Math.sqrt(s*(s-a)*(s-b)*(s-c)); //海伦公式求面积
        }
        public float getLength(){          //固定需求分析求周长
            float a = getDist(x1, y1, x2, y2);
            float b = getDist(x1, y1, x3, y3);
            float c = getDist(x2, y2, x3, y3);
            return a+b+c;
        }
        public Object accept(IVisitor v){  //可扩展需求分析
            return v.visit(this);
        }
    }
    

    着重理解accept()方法,可以看出IVisitor接口中一定定义了多态方法visit(),那为什么把this引用传过去呢?可以这样理解:例如求三角形重心坐标,它的功能一定是在IVisitor的子类实现的,那么该子类一定得知道三角形的三个顶点坐标,因此把this引用传过去,相当于IVisitor的子类可访问Triangle类的成员变量,编制求重心坐标就容易了。

    3.定义访问者接口IVisitor

    interface IVisitor{
        Object visit(Triangle t);
    }
    

    至此为止,有了1、2、3的代码,访问者模式的代码框架就已经构建起来了。如果需求分析没有变化,那么程序一直应用即可;如果需求分析发生变化,则基础功能类不用变化,只要定义IVisitor接口的具体功能实现类就可以了,例如求三角形重心坐标代码如下。
    4.定义重心坐标实现类CenterVisitor

    class Point{
        float x, y;
    }
    class CenterVisitor implements IVisitor{
        public Object visit(Triangle t){
            Point pt = new Point();
            pt.x = (t.x1+t.x2+t.x3)/3;
            pt.y = (t.y1+t.y2+t.y3)/3;
            return pt;
        }
    }
    

    一个简单的测试类如下。

    public class Test3 {
        public static void main(String[] args) {
            IVisitor v = new CenterVisitor();        //定义求重心具体访问者对象
            Triangle t = new Triangle(0,0,2,0,0,2);  //定义三角形对象
            Point pt = (Point)t.accept(v);           //通过访问者对象求三角形重心坐标
            System.out.println(pt.x+"\t"+pt.y);
        }
    }
    

    可以知道,如果再想增加一个求三角形外接圆半径功能,只需再定义一个新类实现IVisitor接口,在该类中完成求外接圆半径功能即可。

    8.命令模式

    顾名思义,命令模式一定是有命令发送者、命令接收者。命令发送者负责发送命令,命令接收者负责接收命令并完成具体的工作。

    命令模式主要针对需要执行的任务或用户提出的请求进行封装与抽象。抽象的命令接口描述了任务或请求的共同特征,而实现则交由不同的具体命令对象完成。每个命令对象都是独立的,它负责完成需要执行的任务,却并不关心是谁调用它。

    命令模式抽象UML类图
    在这里插入图片描述
    命令模式一般有4种角色,如下所示。
    ● ICommander:抽象命令者,是一个接口,规定了用来封装请求的若干个方法。
    ● ConcreteCommander:具体命令发送者,即命令源。它是实现命令接口的类的示例,如上文中的Teacher类。
    ● Invoker:请求者,具体命令的管理与维护类。请求者是一个包含“命令接口”变量的类的示例。请求者中的“命令”接口的变量可以存放任何具体命令的引用,请求者负责调用具体命令,让具体命令执行那些封装了请求的方法。
    ● Receiver:命令接收者,是一个类的示例。该示例负责执行与请求相关的操作,如上文中的Student类。

    考虑老师通知学生打扫卫生的程序描述,具体代码如下。
    1.抽象命令接口ICommand

    interface ICommand{
        public void sweep();
    }
    

    2.命令接收者Student

    class Student{
        public void sweeping(){
            System.out.println("we are sweeping the floor");
        }
    }
    

    在命令模式中,具体工作一定是在接收者中完成的,这一点非常重要。示例中“清扫”工作是由sweeping()方法完成的。
    3.命令发送者Teacher

    class Teacher implements ICommand{
        private Student receiver = null;
        public Teacher(Student receiver){
            this.receiver = receiver;
        }
        public void sweep(){  //发送sweep清扫命令
            receiver.sweeping();
        }
    }
    

    命令发送者类中,一般来说包含命令接收者的引用,表明发送命令的目的地址。所以Teacher类中定义了接收者Student类对象的引用。而实现的抽象接口方法中表明发送命令的具体过程,sweep()中利用方法转发说明具体的清扫工作是由接收者Student对象完成的。
    4.命令请求者类Invoke

    class Invoke{
        ICommand command;
        public Invoke(ICommand command){
            this.command = command;
        }
        public void execute(){
            command.sweep();  //启动命令
        }
    }    
    

    在这里插入图片描述
    在这里插入图片描述
    普通思路是命令发送者直接作用命令接收者,而命令模式思路是在两者之间增加一个请求者类,命令发送者与请求者作用,请求者再与命令接收者作用,请求者起到了一个桥梁的作用

    5.一个简单的测试类

    public class Test {
        public static void main(String[] args)
        {
            Student s = new Student();         //定义接收者
            Teacher t = new Teacher(s);        //定义命令发送者
            Invoke invoke = new Invoke(t);     //将命令请求加到请求者对象中
            invoke.execute();                  //由请求者发送命令
        }
    }
    

    9.装饰器模式

    装饰器模式利用包含代替继承,动态地给一个对象添加一些额外的功能。以消息日志功能为例,其装饰器模式UML类图如图所示
    在这里插入图片描述
    装饰器模式主要有如下4种角色。
    ● 抽象构件角色(Component):
    它是一个接口,封装了将要实现的方法,如ILogger。
    ● 具体构件角色(ConcreteComponent):
    它是多个类,该类实现了Component接口,如FileLogger、ConsoleLogger。
    ● 装饰角色(Decorator):
    它是一个抽象类,该类也实现了Component接口,同时也必须持有接口Component的对象的引用,如事例中Decorator。
    ● 具体的装饰角色(Decorator类的子类,可以有一个,也可以有多个):
    这些类继承了类Decorator,实现了Component接口,描述了具体的装饰过程,如UpLogger、XMLLogger。

    1.抽象装饰器基类Decorator

    abstract class Decorator implements ILogger{
        protected ILogger logger;
        public Decorator(ILogger logger){
            this.logger = logger;
        }
    }
    

    2.具体装饰类

    //信息大写装饰类UpLogger
    class UpLogger extends Decorator{
        public UpLogger(ILogger logger){
            super(logger);
        }
        public void log(String msg) {
            msg = msg.toUpperCase();     //对字符串进行大写装饰
            logger.log(msg);             //再执行已有的日志功能
        }
    }
    
        //XML格式化装饰类XMLLogger
        class XMLLogger extends Decorator{
              public XMLLogger(ILogger logger){
                  super(logger);
              }
              public void log(String msg) {
                  String s = "<msg>\r\n" +
                           "<content>"+msg+"</content>\r\n"+
                            "<time>" + new Date().toString()+ "</time>\r\n"+
                            "</msg>\r\n";
                  logger.log(s);
             }
         }
    

    3.一个简单的测试类

    public class Test {
        public static void main(String[] args)throws Exception {
            ILogger existobj = new FileLogger();     //已有的日志功能
            ILogger newobj= new XMLLogger(existobj); //新的日志装饰类,对existobj装饰
            String s[] = {"how", "are", "you"};        //仿真传送的字符串信息数组
            for(int i=0; i<s.length; i++){
                newobj.log(s[i]);
                Thread.sleep(1000);                  //每隔1 s传送一个新的字符串
            }
            System.out.println("End");
        }
    }
    

    10.组合模式

    文件树型结构示例图
    在这里插入图片描述
    根目录是由两个子目录组成的,第一级子目录由两个文件组成,第二级子目录由两个文件组成,因此树型形式也可以称作组合形式。把叶子节点与目录节点都看成相同性质的节点,只不过目录节点后继节点不为空,而叶子节点后继节点为null。这样就能够对树型结构的所有节点执行相同的操作,这也是组合模式的最大特点。采用组合模式实现文件树型结构的功能,具体代码如下。

    1.定义抽象节点类Node

    abstract class Node{
        protected String name;
        public Node(String name){
            this.name = name;
        }
        public void addNode(Node node)throws Exception{
            throw new Exception("Invalid exception");
        }
        abstract void display();
    }
    

    该类是叶子节点与目录节点的父类,节点名称是name。其主要包括两类方法:一类方法是所有节点具有相同形式、不同内容的方法,这类方法要定义成抽象方法,如display();另一类方法是目录节点必须重写,叶子节点无需重写的方法,相当于为叶子节点提供了默认实现,如addNode()方法,因为叶子对象没有该功能,所以可以通过抛出异常防止叶子节点无效调用该方法。

    2.文件叶子节点类FileNode

    class FileNode extends Node{
        public FileNode(String name){
            super(name);
        }
        public void display(){
            System.out.println(name);
        }
    }
    

    该类是Node的派生类,仅重写display()方法即可。

    3.目录节点类DirectNode

    class DirectNode extends Node{
        ArrayList<Node> nodeList = new ArrayList();
        public DirectNode(String name){
            super(name);
        }
        public void addNode(Node node)throws Exception{
            nodeList.add(node);
        }
        public void display(){
    
            System.out.println(name);
            for(int i=0; i<nodeList.size(); i++){
                nodeList.get(i).display();
            }
        }
    }
    

    该类从Node抽象类派生后,与原DirectNode类相比,主要有以下不同:①由定义两个集合类成员变量转为定义一个集合类成员变量nodeList; ②由定义两个添加方法转为定义一个添加方法addNode(); ③display()方法中,由两个不同元素的循环转为一个对相同性质节点Node的循环。也就是说,原DirectNode中不论是定义成员变量、成员方法,还是方法内部的功能,都要实时考虑叶子节点、目录节点的不同性,因此它的各种定义一定是双份的。而组合模式中认为叶子节点、目录节点是同一性质的节点,因此与原DirectNode类对比,它的各种定义工作一定是减半的,也易于扩充。

    4.一个简单的测试类

    public class Test {
        public static void createTree(Node node)throws Exception{
            File f = new File(node.name);
            File f2[] = f.listFiles();
            for(int i=0; i<f2.length; i++){
                if(f2[i].isFile()){
                    Node node2 = new FileNode(f2[i].getAbsolutePath());
                    node.addNode(node2);
                }
                if(f2[i].isDirectory()){
                    Node node2 = new DirectNode(f2[i].getAbsolutePath());
                    node.addNode(node2);
                    createTree(node2);
                }
            }
        }
        public static void main(String[] args)throws Exception {
            Node start = new DirectNode("d://data");
            createTree(start);
            start.display();
        }
    }
    

    通过该示例,可得组合模式更一般的UML类图,如图所示。共包括以下三种角色。
    在这里插入图片描述
    ● 抽象节点:Node,是一个抽象类(或接口),定义了个体对象和组合对象需要实现的关于操作其子节点的方法,如add()、remove()、display()等。
    ● 叶节点:Leaf,从抽象节点Node派生,由于本身无后继节点,其add()等方法利用Node抽象类中相应的默认实现即可,只需实现与自身相关的remove()、display()等方法即可。
    ● 组合节点:Component,从抽象节点Node派生,包含其他Composite节点或Leaf节点的引用。
    总之,若某应用可形成树型结构,而且形成树型结构后可对叶节点及中间节点进行统一的操作,那么采用组合模式构建应用功能是一个比较好的选择。

    资料来自《Java设计模式深入研究》

    展开全文
  • 一、先来解读:23种设计模式要点 1.单例模式(Singleton Pattern) 2.工厂模式 3.抽象工厂模式(Abstract Factory Pattern) 4.模板方法模式(Template Method Pattern) 5.建造者模式(Builder Pattern) 6....

    一、先来解读:23种设计模式要点

    1.单例模式(Singleton Pattern)

    image

    2.工厂模式

    image

    3.抽象工厂模式(Abstract Factory Pattern)

    image

    4.模板方法模式(Template Method Pattern)

    image

    5.建造者模式(Builder Pattern)

    image

    6.代理模式(Proxy Pattern)

    image

    7.原型模式(Prototype Pattern)

    8.中介者模式

    9.命令模式

    10.责任链模式

    11.装饰模式(Decorator Pattern)

    12.策略模式(Strategy Pattern)

    13.适配器模式(Adapter Pattern)

    14.迭代器模式(Iterator Pattern)

    15.组合模式((Composite Pattern))

    16.观察者模式(Observer Pattern)

    17.门面模式(Facade Pattern)

    image

    18.备忘录模式(Memento Pattern)

    19.访问者模式(Visitor Pattern)

    20.状态模式(复杂)

    image

    21.解释器模式(Interpreter Pattern)(少用)

    22.享元模式(Flyweight Pattern)

    image

    23.桥梁模式(Bridge Pattern)

    … …

    二、六大设计原则(来源:《您的设计模式》)

    • 单一职责原则【Single Responsibility Principle】

    image

    • 里氏替换原则【Liskov Substitution Principle】

    image

    • 依赖倒置原则【Dependence Inversion Principle】

    • 接口隔离原则【Interface Segregation Principle】

    • 迪米特法则【Low Of Demeter】

    image

    • 开闭原则【Open Close Principle】

    image

    三、《大话设计模式》

    通篇是以情景对话的形式,用一个又一个的小故事或者编程示例来组织的。共分为四个部分…

    • 第一部分是面向对象的意义和好处以及几个重要的设计原则,通过小菜面试的失败引出;

    • 第二部分是详细讲解23个设计模式;

    • 第三部分是对设计模式的总结,利用小菜梦想到的超级模式大赛的场景,把所有的面向对象和模式概念都拟人化来趣味总结设计模式之间的异同和关键点;

    • 第四部分是附录,主要是针对对面向对象不熟悉者的一个补充,通过一个例子的演变介绍了类、封装、继承、多态、接口、事件等概念。

    内容偏多,篇幅有限,先上一份总目录看看大概的内容有哪些…

    image

    image

    image

    image

    image

    四、面试常问的设计模式

    • 请列举出在 JDK 中几个常用的设计模式?

    • 什么是设计模式?你是否在你的代码里面使用过任

    • Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式

    • 在 Java 中,什么叫观察者设计模式(observer design pattern

    • 使用工厂模式最主要的好处是什么?在哪里使用?

    • 举一个用 Java 实现的装饰模式(decorator design pattern) ?它是作用于对象层次还是类层次?

    • 在 Java 中,为什么不允许从静态方法中访问非静态变量?

    • 设计一个 ATM 机,请说出你的设计思路?

    • 在 Java 中,什么时候用重载,什么时候用重写?

    • 举例说明什么情况下会更倾向于使用抽象类而不是接口?

    • … …

    image

    image

    最后的内容

    在开头跟大家分享的时候我就说,面试我是没有做好准备的,全靠平时的积累,确实有点临时抱佛脚了,以至于我自己还是挺懊恼的。(准备好了或许可以拿个40k,没做准备只有30k+,你们懂那种感觉吗)

    如何准备面试?

    1、前期铺垫(技术沉积)

    程序员面试其实是对于技术的一次摸底考试,你的技术牛逼,那你就是大爷。大厂对于技术的要求主要体现在:基础,原理,深入研究源码,广度,实战五个方面,也只有将原理理论结合实战才能把技术点吃透。

    下面是我会看的一些资料笔记,希望能帮助大家由浅入深,由点到面的学习Java,应对大厂面试官的灵魂追问,有需要的话就戳这里:蓝色传送门打包带走吧。

    这部分内容过多,小编只贴出部分内容展示给大家了,见谅见谅!

    • Java程序员必看《Java开发核心笔记(华山版)》

    • Redis学习笔记

    • Java并发编程学习笔记

    四部分,详细拆分并发编程——并发编程+模式篇+应用篇+原理篇

    • Java程序员必看书籍《深入理解 ava虚拟机第3版》(pdf版)

    • 大厂面试必问——数据结构与算法汇集笔记

    其他像Spring,SpringBoot,SpringCloud,SpringCloudAlibaba,Dubbo,Zookeeper,Kafka,RocketMQ,RabbitMQ,Netty,MySQL,Docker,K8s等等我都整理好,这里就不一一展示了。

    2、狂刷面试题

    技术主要是体现在平时的积累实用,面试前准备两个月的时间再好好复习一遍,紧接着就可以刷面试题了,下面这些面试题都是小编精心整理的,贴给大家看看。

    ①大厂高频45道笔试题(智商题)

    ②BAT大厂面试总结(部分内容截图)

    ③面试总结

    3、结合实际,修改简历

    程序员的简历一定要多下一些功夫,尤其是对一些字眼要再三斟酌,如“精通、熟悉、了解”这三者的区别一定要区分清楚,否则就是在给自己挖坑了。当然不会包装,我可以将我的简历给你参考参考,如果还不够,那下面这些简历模板任你挑选:

    以上分享,希望大家可以在金三银四跳槽季找到一份好工作,但千万也记住,技术一定是平时工作种累计或者自学(或报班跟着老师学)通过实战累计的,千万不要临时抱佛脚。

    另外,面试中遇到不会的问题不妨尝试讲讲自己的思路,因为有些问题不是考察我们的编程能力,而是逻辑思维表达能力;最后平时要进行自我分析与评价,做好职业规划,不断摸索,提高自己的编程能力和抽象思维能力。

    g-ockdfMZt-1625158372181)]

    以上分享,希望大家可以在金三银四跳槽季找到一份好工作,但千万也记住,技术一定是平时工作种累计或者自学(或报班跟着老师学)通过实战累计的,千万不要临时抱佛脚。

    另外,面试中遇到不会的问题不妨尝试讲讲自己的思路,因为有些问题不是考察我们的编程能力,而是逻辑思维表达能力;最后平时要进行自我分析与评价,做好职业规划,不断摸索,提高自己的编程能力和抽象思维能力。

    以上文章中,提及到的所有的笔记内容、面试题等资料,均可以免费分享给大家学习,有需要的话就戳这里打包带走吧。

    展开全文
  • 对于任何一个Java开发人员来说,必须学会面向对象的设计原则和各种设计模式的知识。但有一些关于面向对象设计原则、设计模式和最佳实践的书籍,只有少数几本书能做到真正在讲解这方面内容。 设计原则和设计模式,...
  • 经典的java 23种设计模式及具体例子

    万次阅读 多人点赞 2016-07-10 09:43:50
    经典的java 23种设计模式及具体例子
  • 为什么要学习设计模式?...对于想学设计模式的程序员来说,我的建议是先对Java的基本知识有所了解,甚至于比较熟悉之后,再看设计模式。不然的话,就可能印象不深刻。对于已经熟悉Java的基本知识的程序员,我的建议是
  • Java 日志框架解析:设计模式、性能

    千次阅读 2017-09-28 10:00:54
    在平常的系统开发中,日志起到了重要的作用,日志写得好对于线上问题追踪有着很大的帮助。一个好的日志框架,既要方便易用,也要有较好的性能,减少日志输出对系统内存、CPU 的影响。 研究一款开源项目,学到的不...
  • 距离《重学Java设计模式》这本PDF书籍???? 7月12日出炉以后,因为工作内容时间加长,已经有两周没有推文了,对我自己来说还蛮不习惯的。也同时让我感悟到,除了上学阶段以后,想给自己投入时间真的不容易。 就像说...
  • 系列文章目录 提示:这里可以添加系列文章...例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。 提示:以下是本篇文章正文内容,下面案例
  • java设计模式】之 工厂(Factory)模式

    万次阅读 多人点赞 2016-04-23 10:48:57
    1.工厂模式的定义  工厂模式使用的频率非常高,我们在开发中总能见到它们的身影。其定义为:Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method...
  • 什么是设计模式:【每天一个java设计模式(零)】 - 设计模式基本概念及七大设计原则 创建型模式 工厂模式:【每天一个java设计模式(一)】 - 工厂模式 抽象工厂模式: 【每天一个java设计模式(二)】 - 抽象...
  • 常用设计模式及其 Java 实现

    千次阅读 2018-08-05 14:06:05
    本文按照创建型、结构型、行为型三大类,总结了常见的 24 种设计模式的使用要点,包括适用场景、解决方案、及其相应的 Java 实现。 作者:王克锋 出处:https://kefeng.wang/2018/04/16/design-patterns/ 版权...
  • java 23种设计模式详解

    万次阅读 多人点赞 2016-07-01 14:45:14
    设计模式的分类总体来说设计模式分为三大类:创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合...
  • Java常用设计模式详解及其优缺点

    千次阅读 多人点赞 2019-03-18 10:17:57
    简单工厂模式、工厂方法模式、抽象工厂模式、静态代理模式、JDK动态代理模式、cglib动态代理模式、策略模式、模板模式、装饰器模式、观察者模式 简单工厂模式 简单工厂模式又称为静态工厂方法模式,涉及到三个角色...
  • JAVA设计模式之享元模式

    万次阅读 多人点赞 2014-04-04 07:34:52
    解释一下概念:也就是说在一个系统中如果有多个相同的对象,那么只共享一份就可以了,不必每个都去实例化一个对象。比如说一个文本系统,每个字母定一个对象,那么大小写... 在Flyweight模式中,由于要产生各种各样
  • 详述「设计模式」及其 Java 实现

    千次阅读 2017-10-18 18:05:56
    设计模式首先,我们来看看百度和维基百科对设计模式的解释: 百度百科:设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。 Wikipedia:A design pattern is the re-usable form of a...
  • 大话设计模式、UML、设计模式Java版完全总结

    万次阅读 多人点赞 2018-03-23 09:54:09
    ≈读完《大话设计模式》 ),注意是笔记形式,优先适合于对设计模式有一定了解的读者,希望短时间快速温习的读者,同时也对所有设计模式添加了完整代码诠释与注释,方便初学者的理解,另外,文章末尾有对所有设计...
  • 有些情况下,一个对象会有一些重要的性质,在它们没有恰当的值之前,对象不能作为一个完整的产品使用。比如,一个电子邮件有发件人地址、收件人地址、主题、内容、附录等部分,而在起码的收件人地址得到赋值之前,...
  • java开发中的常用的设计模式

    万次阅读 多人点赞 2018-05-28 13:03:34
    设计模式(Design Patterns) ——可复用面向对象软件的基础设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人...
  • 这篇将会介绍装饰者模式(Decorator Pattern),装饰者模式也称为包装模式(Wrapper Pattern),结构型模式之一,其使用一种对客户端透明的方式来动态的扩展对象的功能,同时它也是继承关系的一种替代方案之一,但比...
  • java设计模式之模板方法模式

    千次阅读 2016-05-21 06:46:13
    本博客摘录于秦小波老师写的《设计模式之禅》 模板方法模式 汽车厂造悍马 假设我们是一个汽车公司,现在有客户来了,要求我们造悍马! 既然上级下来命令那就造呗,但是造悍马你得告诉我们汽车有什么功能...
  • Java设计模式】抽象工厂

    千次阅读 多人点赞 2019-07-07 09:36:19
    定义:抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口 扩展:无须指定它们具体的类 类型:创建型 抽象工厂能够将一组具有同一主题和单独的工厂封装起来,在正常使用中,客户端程序创建抽象工厂的具体实现...
  • Java设计模式学习总结

    千次阅读 2018-07-08 10:54:59
    设计模式 创建型模式 创建型模式 创建型模式的作用就是创建对象,说到创建一个对象,熟悉的就是 new 一个对象,然后 set 相关属性。但是,在很多场景下,我们需要给客户端提供更加友好的创建对象的方式,尤其...
  • Java8-20-lambda 设计模式

    万次阅读 2019-03-26 22:14:10
    文章目录使用 Lambda 重构面向对象的设计模式语言特性对于技术的推动lambda 对于设计模式的影响策略模式内容应用场景使用Lambda表达式模板方法例子使用Lambda表达式观察者模式例子使用Lambda表达式责任链模式代码...
  • java中四种常见的设计模式

    千次阅读 2018-09-08 21:57:25
    1、单例​模式:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点 主要: 饿汉式(线程安全,调用效率高,但是不能延时加载) 懒汉式(线程安全,调用效率不高,但是可以延时加载) 其他: 双重...
  • JAVA设计模式--状态模式

    万次阅读 2017-02-11 11:18:13
    状态(State))模式,又称状态对象(Pattern of Objects for States)模式,是一种对象的行为模式。状态模式允许一个对象在其内部状态改变的时候改变其行为。这个对象看上去就像是改变了它的类一样。 状态模式的...
  • 23种设计模式介绍以及在Java中的实现

    万次阅读 多人点赞 2016-04-24 01:06:53
    文本详细总结了23种设计模式以及他们如何应用到java中,并提供了大量的示例代码。 文章中的示例源码在github上:https://github.com/anxpp/JavaDesignPattern。 如果本文对您有所帮助,请帮忙点个赞扩散一下,谢谢!
  • 这篇来讲一下观察者模式,观察者模式在实际项目中使用的也是非常频繁的,它常用的地方是GUI系统、订阅——发布系统。因为这个模式的一个重要作用就是解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。以GUI系统...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 331,652
精华内容 132,660
关键字:

java最重要的设计模式

java 订阅