图解设计模式

掌握设计模式的层次
第1层:刚开始学编程不久,听说过什么是设计模式
第2层:有很长时间的编程经验,自己写了很多代码,其中用到了设计模式,但是自己却不知道
第3层:学习过了设计模式,发现自己已经在使用了,并且发现了一些新的模式挺好用的
第4层:阅读了很多别人写的源码和框架,在其中看到别人设计模式,并且能够领会设计模式的精妙和带来的好处。
第5层:代码写着写着,自己都没有意识到使用了设计模式,并且熟练的写了出来。

设计模式介绍
1.设计模式是程序员在面对同类软件工程设计问题所总结出来的有用的经验,模式不是代码,
  而是某类问题的通用解决方案,设计模式(Design pattern)代表了最佳的实践。
  这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
2.设计模式的本质提高软件的维护性,通用性和扩展性,并降低软件的复杂度。
3.<<设计模式>>是经典的书,作者是 Erich Gamma、Richard Helm、RalphJohnson和John Vlissides Design(俗称“四人组GOF”)
4.设计模式并不局限于某种语言,java,php,c++都有设计模式.

设计模式类型
设计模式分为三种类型,共23种
1.创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式。
2.结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
3.行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、
  解释器模式(Interpreter模式)、状态模式、策略模式、职责链模式(责任链模式)。
注意:不同的书籍上对分类和名称略有差别

单例设计模式介绍
   所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,
并且该类只提供一个取得其对象实例的方法(静态方法)。
   比如Hibernate的SessionFactory,它充当数据存储源的代理,并负责创建Session对象。SessionFactory并不是轻量级的,
一般情况下,一个项目通常只需要一个SessionFactory就够,这是就会使用到单例模式。

单例设计模式八种方式:

1.饿汉式(静态常量)   --可以用,可以会造成内存浪费
2.饿汉式(静态代码块)  --可以用,可以会造成内存浪费
3.懒汉式(线程不安全)        --不推荐
4.懒汉式(线程安全,同步方法)  --不推荐
5.懒汉式(线程安全,同步代码块) --不能使用
5.双重检查    --推荐
7.静态内部类  --推荐
8.枚举       --推荐

饿汉式(静态常量)应用实例步骤如下:
1.构造器私有化(防止new)
2.类的内部创建对象
3.向外暴露一个静态的公共万法。getInstance
4.代码实现 Singleton1()
优缺点说明:
1.优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
2.缺点:在类装载的时候就完成实例化,没有达到Lazy Loading(懒加载)的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费
3.这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,在单例模式中大多数都是调用getInstance方法,
  但是导致类装载的原因有很多种,因此不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance就没有达到lazy loading的效果
4.结论:这种单例模式可用,可能造成内存浪费

饿汉式(静态代码块)
代码实现  Singleton2
优缺点说明:
1.这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。优缺点和上面是一样的。
2.结论:这种单例模式可用,但是可能造成内存浪费

懒汉式(线程不安全)
代码实现  Singleton3
优缺点说明:
1.起到了Lazy Loading(懒加载)的效果,但是只能在单线程下使用。
2.如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,
  另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式
3.结论:在实际开发中,不要使用这种方式.

懒汉式(线程安全,同步方法)
代码实现  Singleton4
优缺点说明:
1.解决了线程不安全问题
2.效率太低了,每个线程在想获得类的实例时候,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,
  后面的想获得该类实例,直接return就行了。方法进行同步效率太低
3.结论:在实际开发中,不推荐使用这种方式

懒汉式(线程安全,同步代码块)
代码实现  Singleton4
优缺点说明:
1.这种方式,本意是想对第四种实现方式的改进,因为前面同步方法效率太低,改为同步产生实例化的的代码块
2.但是这种同步并不能起到线程同步的作用。跟第3种实现方式遇到的情形一致,假如一个线程进入了if (singleton == null)判断语句块,
  还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例
3.结论:在实际开发中,不能使用这种方式

双重检查
代码实现  Singleton6
优缺点说明:
1.Double-Check概念是多线程开发中常使用到的,如代码中所示,我们进行了两次if (singleton == null)检查,这样就可以保证线程安全了。
2.这样,实例化代码只用执行一次,后面再次访问时,判断if (singleton == null),直接return实例化对象,也避免的反复进行方法同步.
3.线程安全;延迟加载;效率较高
4.结论:在实际开发中,推荐使用这种单例设计模式

静态内部类
代码实现  Singleton7
优缺点说明:
1.这种方式采用了类装载的机制来保证初始化实例时只有一个线程。
2.静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getlnstance方法,
  才会装载SingletonInstance类,从而完成Singleton的实例化。
3.类的静态属性只会在第一次加载类的时候初始化,所以在这里,JⅣM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
4.优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
5.结论:推荐使用.

枚举
代码实现  Singleton8
优缺点说明:
1.这借助JDK1.5中添加的枚举来实现单例模式。不仅能避免迄线程同步问题,而且还能防止反序列化重新创建新的对象。
2.这种方式是Effective Java作者Josh Bloch 提倡的方式
3.结论:推荐使用

单例模式注意事项和细节说明
1.单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能
2.当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new
3.单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、
  频繁访问数据库或文件的对象(比如数据源、session工厂等)


工厂模式

看一个披萨的项目:要便于披萨种类的扩展,要便于维护
1.披萨的种类很多(比如GreekPizz、CheesePizz等)
2.披萨的制作有prepare,bake,cut, box
3.完成披萨店订购功能。

代码实例: pizzastore

传统的方式的优缺点
1.优点是比较好理解,简单易操作。
2.缺点是违反了设计模式的ocp原则,即对扩展开放,对修改关闭。即当我们给类增加新功能的时候,尽量不修改代码,或者尽可能少修改代码.
3.比如我们这时要新增加一个Pizza的种类(Pepper披萨),我们需要做如下修改.

改进的思路分析
分析:修改代码可以接受,但是如果我们在其它的地方也有创建Pizza的代码,就意味着,也需要修改,而创建Pizza的代码,往往有多处。
思路:把创建Pizza对象封装到一个类中,这样我们有新的Pizza种类时,只需要修改该类就可,其它有创建到Pizza对象的代码就不需要修改了>简单工厂模式

简单工厂模式
基本介绍
1.简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式
2.简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为(代码)
3.在软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式.

工厂方法模式
看一个新的需求
披萨项目新的需求:客户在点披萨时,可以点不同口味的披萨,比如北京的奶酪pizza、北京的胡椒pizza或者是伦敦的奶酪pizza、伦敦的胡椒pizza。
思路1: 使用简单工厂模式,创建不同的简单工厂类,比如BJPizzaSimpleFactory、LDPizzaSimpleFactory 等等.
      从当前这个案例来说,也是可以的,但是考虑到项目的规模,以及软件的可维护性、可扩展性并不是特别好
思路2: 使用工厂方法模式

工厂方法模式介绍
工厂方法模式设计方案:将披萨项目的实例化功能抽象成抽象方法,在不同的口味点餐子类中具体实现。
工厂方法模式:定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类。

抽象工厂模式
基本介绍
1.抽象工厂模式:定义了一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类
2.抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。
3.从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象)。
4.将工厂抽象成两层,AbsFactory(抽象工厂)和具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。
  这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。

工厂模式小结
1.工厂模式的意义
将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦。从而提高项目的扩展和维护性。
2.三种工厂模式(简单工厂模式,工厂方法模式,抽象工厂模式)
3.设计模式的依赖抽象原则
>创建对象实例时,不要直接new类,而是把这个new类的动作放在一个工厂的方法中,并返回。有的书上说,变量不要直接持有具体类的引用。
>不要让类继承具体类,而是继承抽象类或者是实现interface(接口)
>不要覆盖基类中已经实现的方法。


原型模式

克隆羊问题
现在有一只羊tom,姓名为: tom,年龄为:1,颜色为:白色,请编写程序创建和tom羊属性完全相同的10只羊。

传统方法:prototype

传统的方式的优缺点
1.优点是比较好理解,简单易操作。
2.在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低
3.总是需要重新初始化对象,而不是动态地获得对象运行时的状态,不够灵活
4.改进的思路分析
思路: Java中object类是所有类的根类,Object类提供了一个clone()方法,该方法可以将一个Java对象复制一份,
     但是需要实现clone的Java类必须要实现一个接口Cloneable,该接口表示该类能够复制且具有复制的能力=→>原型模式

基本介绍
1.原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
2.原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节
3.工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即对象.clone()
4.形象的理解:孙大圣拔出猴毛,变出其它孙大圣

>原理结构图说明
1. Prototype:原型类,声明一个克隆自己的接口
2. ConcretePrototype:具体的原型类,实现一个克隆自己的操作
3. Client让me个原型对象克隆自己,从而创建一个新的对象(属性一样)

浅拷贝基本介绍
1.对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
2.对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,
  也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。
  在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值
3.前面我们克隆羊就是浅拷贝
4.浅拷贝是使用默认的clone()方法来实现  sheep = (Sheep) super.clone();

深拷贝基本介绍
1.复制对象的所有基本数据类型的成员变量值
2.为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,
  直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝
3.深拷贝实现方式1:重写clone方法来实现深拷贝
4.深拷贝实现方式2:通过对象序列化实现深拷贝

原型模式的注意事项和细节
1.创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
2.不用重新初始化对象,而是动态地获得对象运行时的状态
3.如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码
4.在实现深克隆的时候可能需要比较复杂的代码
5.缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了ocp原则,这点请同学们注意.


建造者模式
盖房项目需求
1.需要建房子:这一过程为打桩、砌墙、封顶
2.房子有各种各样的,比如普通房,高楼,别墅,各种房子的过程虽然一样,但是要求不要相同的.
3.请编写程序,完成需求.

传统方式解决盖房需求问题分析
1.优点是比较好理解,简单易操作。
2.设计的程序结构,过于简单,没有设计缓存层对象,程序的扩展和维护不好.也就是说,这种设计方案,
  把产品(即:房子)和创建产品的过程(即:建房子流程)封装在一起,耦合性增强了。
3.解决方案:将产品和产品建造过程解耦=>建造者模式.

基本介绍
1.建造者模式(Builder Pattern)又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),
  使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。
2.建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。

建造者模式的四个角色
1.Product(产品角色):一个具体的产品对象。
2.Builder(抽象建造者):创建一个Product对象的各个部件指定的接口/抽象类
3.ConcreteBuilder(具体建造者):实现接口,构建和装配各个部件。
4.Director(指挥者):构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象。
  它主要有两个作用, 一是:隔离了客户与对象的生产过程, 二是:负责控制产品对象的生产过程。

建造者模式的注意事项和细节
1.客户端(使用程序)不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象
2.每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象
3.可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程
4.增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”
5.建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
6.如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,因此在这种情况下,要考虑是否选择建造者模式.
7.抽象工厂模式vs建造者模式
  抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式不需要关心构建过程,
  只关心什么产品由什么工厂生产即可。而建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品


适配器模式
泰国旅游使用插座问题
现实生活中的适配器例子
泰国插座用的是两孔的(欧标),可以买个多功能转换插头(适配器),这样就可以使用了。

基本介绍
1.适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,
  主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)
2.适配器模式属于结构型模式
3.主要分为三类:类适配器模式、对象适配器模式、接口适配器模式

工作原理:
1.适配器模式:将一个类的接口转换成另一种接口.让原本接口不兼容的类可以兼容
2.从用户的角度看不到被适配者,是解耦的
3.用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法
4.用户收到反馈结果,感觉只是和目标接口交互,如图

类适配器模式介绍
基本介绍: Adapter类,通过继承src类,实现 dst类接口,完成src->dst的适配。
类适配器模式应用实例
1.应用实例说明
  以生活中充电器的例子来讲解适配器,充电器本身相当于Adapter,220v交流电相当于src(即被适配者),我们的目dst(即目标)是5V直流电
2.思路分析图解
3.代码实现 classadapter

类适配器模式注意事项和细节
1.Java是单继承机制,所以类适配器需要继承src类这一点算是一个缺点,因为这要求dst必须是接口,有一定局限性;
2.src类的方法在Adapter中都会暴露出来,也增加了使用的成本。
3.由于其继承了src类,所以它可以根据需求重写src类的方法,使得Adapter的灵活性增强了。

对象适配器模式介绍
1.基本思路和类的适配器模式相同,只是将Adapter类作修改,不是继承src类,而是持有src类的实例,
  以解决兼容性的问题。即:持有src类,实现dst类接口,完成src->dst的适配
2.根据“合成复用原则”,在系统中尽量使用关联关系来替代继承关系。
3.对象适配器模式是适配器模式常用的一种

对象适配器模式注意事项和细节
1.对象适配器和类适配器其实算是同一种思想,只不过实现方式同。根据合成复用原则,使用组合替代继承,
  所以它解决了类适配器必须继承src的局限性问题,也不再要求dst必须是接口。
2.使用成本更低,更灵活。

接口适配器模式介绍
1.一些书籍称为:适配器模式(Default Adapter Pattern)或缺省适配器模式。
2.当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),
  那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求
3.适用于一个接口不想使用其所有的方法的情况。

接口适配器模式应用实例
1.Android中的属性动画ValueAnimator类可以通过addListentr(AnimatorListener listener)方法添加监听器,那么常规写法如右:
2.有时候我们不想实现Animator.AnimatorListener接口的全部方法,我们只想监听onAnimationStart,我们会如下写
3.AnimatorListenerAdapter类,就是一个接口适配器,代码如右图:它空实现了Animator.AnimatorListener类(src)的所有方法.
4.AnimatorListener是一个接口.
5.程序里的匿名内部类就是Listener 具体实现类

适配器模式的注意事项和细节
1.三种命名方式,是根据 src是以怎样的形式给到Adapter(在Adapter里的形式)来命名的。
2.类适配器:以类给到,在Adapter里,就是将src当做类,
  继承对象适配器:以对象给到,在Adapter里,将src作为一个对象,持有
  接口适配器:以接口给到,在Adapter里,将src作为一个接口,实现
3.Adapter模式最大的作用还是将原本不兼容的接口融合在一起工作。
4.实际开发中,实现起来不拘泥于我们讲解的三种经典形式


桥接模式

手机操作问题
现在对不同手机类型的
不同品牌实现操作编程(比如:开机、关机、上网,打电话等),如图:

传统方案解决手机操作问题分析
1.扩展性问题(类爆炸),如果我们再增加手机的样式(旋转式),就需要增加各个品牌手机的类,同样如果我们增加一个手机品牌,也要在各个手机样式类下增加。
2.违反了单一职责原则,当我们增加手机样式时,要同时增加所有品牌的手机,这样增加了代码维护成本.
3.解决方案-使用桥接模式

基本介绍
1.桥接模式(Bridge模式)是指:将实现有抽象放在两个不同的类层次中,使两个层次可以独立改变。
2.是一种结构型设计模式
3.Bridge模式基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。它的主要特点是把抽象(Abstraction)与行为实现
  (Implementation)分离开来,从而可以保持各部分的独立性以及应对他们的功能扩展

桥接模式的注意事项和细节
1.实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性,让抽象部分和实现部分独立开来,这有助于系统进行分层设计,从而产生更好的结构化系统。
2.对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部分由具体业务来完成。
3.桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本。
4.桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程
5.桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围有一定的局限性,即需要有这样的应用场景。

桥接模式其它应用场景
1.对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用.
2.常见的应用场景:
-JDec驱动程序
-银行转账系统
车专账分类:网上转账,柜台转账,AMT专账
转账用户类型:普通用户,银卡用户,金卡用户..-消息管理
消息类型:即时消息,延时消息
消息分类:手机短信,邮件消息,QQ消息....


装饰者设计模式
星巴克咖啡订单项目(咖啡馆):
1.咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack(浓缩咖啡)、LongBlack(美式咖啡)、Decaf(无因咖啡)
2.调料:Milk、Soy(豆浆)、Chocolate
3.要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便
4.使用OO的来计算不同种类咖啡的费用:客户可以点单品咖啡,也可以单品咖啡+调料组合。

方案1-解决星巴克咖啡订单问题分析
1.Drink是一个抽象类,表示饮料
2.des就是对咖啡的描述,比如咖啡的名字
3.cost()方法就是计算费用,Drink类中做成一个抽象方法.
4.Decaf就是单品咖啡,继承Drink,并实现cost
5.Espress && Milk就是单品咖啡+调料,这个组合很多
6.问题:这样设计,会有很多类,当我们增加一个单品咖啡,或者一个新的调料,类的数量就会倍增,就会出现类爆炸

方案2-解决星巴克咖啡订单(好点)
前面分析到方案1因为咖啡单品+调料组合会造成类的倍增,因此可以做改进,将调料内置到Drink类,这样就不会造成类数量过多。从而提高项目的维护性(如图)
说明: milk,soy,chocolate 可以设计为Boolean,表示是否要添加相应的调料.

方案2-的问题分析
1.方案2可以控制类的数量,不至于造成很多的类
2.在增加或者删除调料种类时,代码的维护量很大
3.考虑到用户可以添加多份调料时,可以将hasMilk返回一个对应int
4.考虑使用装饰者模式

装饰者模式定义
1.装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)
2.这里提到的动态的将新功能附加到对象和ocp原则,在后面的应用实例上会以代码的形式体现,注意体会。

装饰者模式原理
1.装饰者模式就像打包一个快递
主体:比如:陶瓷、衣服(Component)
包装:比如:报纸填充、塑料泡沫、纸板、木板(Decorator)
2.Component  主体:比如类似前面的Drink
3.ConcreteComponent和Decorator ConcreteComponent:具体的主体,比如前面的各个单品咖啡 Decorator:装饰者,比如各调料.
4.在如图的Component与ConcreteComponent之间,如果ConcreteComponent类很多,还可以设计一个缓冲层,将共有的部分提取出来,抽象层一个类。


组合模式
看一个学校院系展示需求
编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系。
如图:
-------清华大学--------
-------计算机学院-
计算机科学与技术
软件工程
网络工程
-------信息工程学院---------
通信工程
信息工程

传统方案解决学校院系展示存在的问题分析
1.将学院看做是学校的子类,系是学院的子类,这样实际上是站在组织大小来进行分层次的
2.实际上我们的要求是:在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系,因此这种方案,
  不能很好实现的管理的操作,比如对学院、系的添加,删除,遍历等
3.解决方案:把学校、院、系都看做是组织结构,他们之间没有继承的关系,而是一个树形结构,可以更好的实现管理操作。=>组合模式

基本介绍
1.组合模式(Composite Pattern),又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示“整体-部分”的层次关系。
2.组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
3.这种类型的设计模式属于结构型模式。
4.组合模式使得用户对单个对象和组合对象的访问具有一致性,即:组合能让客户以一致的方式处理个别对象以及组合对象

对原理结构图的说明-即(组合模式的角色及职责)
1.Companet :这是组合中对象声明接口,在适当情况下s实现所有类共有的接口默认行为,用于访问和管理Compnant子部件,Componant可以是抽象类或者接口
2.Leaf :在组合中表示叶子节点,叶子节点没有子节点
3.Camposite :非叶子节点,用于存储子部件。在Comparnent接口中实现子部件的相关操作,比如增加(add),删除..

解决的问题
1.组合模式解决这样的问题,当我们的要处理的对象可以生成一颗树形结构,而我们要对树上的节点和叶子进行操作时,它能够提供一致的方式,而不用考虑它是节点还是叶子

组合模式的注意事项和细节
1.简化客户端操作。客户端只需要面对一致的对象而不用考虑整体部分或者节点叶子的问题。
2.具有较强的扩展性。当我们要更改组合对象时,我们只需要调整内部的层次关系,客户端不用做出任何改动.
3.方便创建出复杂的层次结构。客户端不用理会组合里面的组成细节,容易添加节点或者叶子从而创建出复杂的树形结构
4.需要遍历组织机构,或者处理的对象具有树形结构时,非常适合使用组合模式.
5.要求较高的抽象性,如果节点和叶子有很多差异性的话,比如很多方法和属性都不一样,不适合使用组合模式

外观模式

组建一个家庭影院:
DVD播放器、投影仪、自动屏幕、环绕立体声、爆米花机,要求完成使用家庭影院的功能,其过程为:
.直接用遥控器∶统筹各设备开关
.开爆米花机
.放下屏幕
.开投影仪
.开音响
.开DVD
.选dvd
.去拿爆米花
.调暗灯光
.播放
·观影结束后,关闭各种设备

传统方式解决影院管理问题分析
1.在ClientTest的main方法中,创建各个子系统的对象,并直接去调用子系统(对象)相关方法,会造成调用过程混乱,没有清晰的过程
2.不利于在ClientTest中,去维护对子系统的操作
3.解决思路:定义一个高层接口,给子系统中的一组接口提供一个一致的界面(比如在高层接口提供四个方法ready, play, pause, end ),用来访问子系统中的一群接口
4.也就是说就是通过定义一个一致的接口(界面类),用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节=→>外观模式

基本介绍
1.外观模式(Facade),也叫“过程模式:外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
2.外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节

原理类图的说明(外观模式的角色)
1.外观类(Facade):为调用端提供统一的调用接口,外观类知道哪些子系统负责处理请求,从而将调用端的请求代理给适当子系统对象
2.调用者(Cliert):外观接口的调用者
3.子系统的集合:指模块或者子系统,处理Facade对象指派的任务,他是功能的实际提供者

传统方式解决影院管理说明
1.外观模式可以理解为转换一群接口,客户只要调用一个接口,而不用调用多个接口才能达到目的。
  比如:在pc上安装软件的时候经常有一键安装选项(省去选择安装目录、安装的组件等等),
  还有就是手机的重启功能(把关机和启动合为一个操作)。
2.外观模式就是解决多个复杂接口带来的使用困难,起到简化用户操作的作用

外观模式的注意事项和细节心
1.外观模式对外屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性
2.外观模式对客户端与子系统的耦合关系,让子系统内部的模块更易维护和扩展
3.通过合理的使用外观模式,可以帮我们更好的划分访问的层次
4.当系统需要进行分层设计时,可以考虑使用Facade模式
5.在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为新系统开发一个Facade类,
  来提供遗留系统的比较清晰简单的接口,让新系统与Facade类交互,提高复用性
6.不能过多的或者不合理的使用外观模式,使用外观模式好,还是直接调用模块好。要以让系统有层次,利于维护为目的。

享元模式
展示网站项目需求
小型的外包项目,给客户A做一个产品展示网站,客户A的朋友感觉效果不错,也希望做这样的产品展示网站,但是要求都有些不同:
1.有客户要求以新闻的形式发布
2.有客户人要求以博客的形式发布
3.有客户希望以微信公众号的形式发布

传统方案解决网站展现项目
1.直接复制粘贴一份,然后根据客户不同要求,进行定制修改
2.给每个网站租用一个空间

传统方案解决网站展现项目-问题分析
1)需要的网站结构相似度很高,而且都不是高访问量网站,如果分成多个虚拟空间来处理,相当于一个相同网站的实例对象很多,造成服务器的资源浪费
2)解决思路:整合到一个网站中,共享其相关的代码和数据,对于硬盘、内存、CPU数据库空间等服务器资源都可以达成共享,减少服务器资源
3)对于代码来说,由于是一份实例,维护和扩展都更加容易4)上面的解决思路就可以使用享元模式来解决

基本介绍
1.辜元模式(Flyweight Pattern)也叫蝇量模式:运用共享技术有效地支持大量细粒度的对象
2.常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来用,
  避免重新创建,如果没有我们需要的,则创建一个
3.享元模式能够解决重复对象的内存浪费的问题,当系统中有大量相似对象,需要缓冲池时。不需总是创建新对象,可以从缓冲池里拿。这样可以降低系统内存,同时提高效率
4.享元模式经典的应用场景就是池技术了,String常量池、数据库连接池、缓冲池等等都是享元模式的应用,享元模式是池技术的重要实现方式

对原理图的说明-即(模式的角色及职责)
1.FlyWeight 是抽象的享元角色,他是产品的抽象类,同时定义出对象的外部状态和内郄状态(后面介绍)的接口或实现
2.ConcreteFlyWeight是具体的享元角色,是具体的产品类,实现抽象角色定义相关业务
3.UnSharedConcreteFlyWeight是不可共享的角色,一般不会出现在享元工厂·
4.FlyWeightFactory享元工厂类,用于构建一个池容器(集合),同时提供从池中获取对象方法

内部状态和外部状态
   比如围棋、五子棋、跳棋,它们都有大量的棋子对象,围棋和五子棋只有黑白两色,跳棋颜色多点,所以棋子颜色就是棋子的内部状态﹔
而各个棋子之间的差别就是位置的不同,当我们落子后,落子颜色是定的,但位置是变化的,所以棋子坐标就是棋子的外部状态
1.享元模式提出了两个要求:细粒度和共享对象。这里就涉及到内部状态和外部状态了,即将对象的信息分为两个部分:内部状态和外部状态
2.内部状态指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变
3.外部状态指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态。
4.举个例子:围棋理论上有361个空位可以放棋子,每盘棋都有可能有两三百个棋子对象产生,因为内存空间有限,
  一台服务器很难支持更多的玩家玩围棋游戏,如果用享元模式来处理棋子,那么棋子对象就可以减少到只有两个实例,
  这样就很好的解决了对象的开销问题

享元模式的注意事项和细节
1.在享元模式这样理解,“享”就表示共享,“元”表示对象
2.系统中有大量对象,这些对象消耗大量内存,并且对象的状态大部分可以外部化时,我们就可以考虑选用享元模式
3.用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象,用HashMap/HashTable存储
4.享元模式大大减少了对象的创建,降低了程序内存的占用,提高效率
5.享元模式提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变,这是我们使用享元模式需要注意的地方.
6.使用享元模式时,注意划分内部状态和外部状态,并且需要有一个工厂类加以控制。
7.享元模式经典的应用场景是需要缓冲池的场景,比如 String常量池、数据库连接池


代理模式

代理模式的基本介绍
1.代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象.
   这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
2.被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象
3.代理模式有不同的形式,主要有三种静态代理、动态代理(jdk代理,接口代理)和Cglib代理(可以在内存动态创建对象,而不需要实现接口,它是属于动态代理的范畴)。

静态代码模式的基本介绍
静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类
应用实例
具体要求
1.定义一个接口:ITeacherDao
2.目标对象TeacherDAO实现接口ITeacherDAO
3.使用静态代理方式,就需要在代理对象TeacherDAOProxy中也实现ITeacherDAO
4.调用的时候通过调用代理对象的方法来调用目标对象.
5.特别提醒:代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法。

静态代理优缺点
1.优点:在不修改目标对象的功能前提,能通过代理对象对目标功能扩展中
2.缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类
3.一旦接口增加方法,目标对象与代理对象都要维护

动态代理
动态代理模式的基本介绍
1.代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
2.代理对象的生成,是利用JDK的APl,动态的在内存中构建代理对象
3.动态代理也叫做:JDK代理、接口代理
JDK中生成代理对象的API
1.代理类所在包:java.lang.reflect.Proxy
2.JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h )

Cglib代理模式的基本介绍
1.静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,
  这个时候可使用目标对象子类来实现代理-这就是cglib代理
2.Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展,有些书也将cglib代理归属到动态代理。
3.Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP,实现方法拦截
4.在AOP编程中如何选择代理模式:
  1.目标对象需要实现接口,用JDK代理
  2.目标对象不需要实现接口,用Cglib代理
5.Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类

Cglib代理模式实现步骤
1.需要引入cglib的jar文件
2.在内存中动态构建子类,注意代理的类不能为final,否则报错java.lang.lllegalArgumentException:
3.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.

代理模式(Proxy)的变体
几种常见的代理模式介绍一几种变体
1.防火墙代理
内网通过代理穿透防火墙,实现对公网的访问。
2.缓存代理
比如:当请求图片文件等资源时,先到缓存代理取,如果取到资源则ok,如果取不到资源,再到公网或者数据库取,然后缓存。
3.远程代理
远程对象的本地代表,通过它可以把远程对象当本地对象来调用。远程代理通过网络和真正的远程对象沟通信息。
4.同步代理:主要使用在多线程编程中,完成多线程间同步工作

模版方法模式
编写制作豆浆的程序,说明如下:
1.制作豆浆的流程选材--->添加配料--->浸泡--->放到豆浆机打碎
2.通过添加不同的配料,可以制作出不同口味的豆浆
3.选材、浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样的
4.请使用模板方法模式完成(说明:因为模板方法模式,比较简单,很容易就想到这个方案,因此就直接使用,不再使用传统的方案来引出模板方法模式)

基本介绍一
1.模板方法模式(Template Method Pattern),又叫模板模式(Template Pattern),在一个抽象类公开定义了执行它的方法的模板。
  它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
2.简单说,模板方法模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤
3.这种类型的设计模式属于行为型模式。

对原理类图的说明-即(模板方法模式的角色及职责)
1.AbstractClass抽象类,类中实现了模板方法(template),定义了算法的骨架,具体子类需要去实现其它的抽象方法operationr2,3,4
2.ConcreteClass实现抽象方法operationr2,3,4,以完成算法中特点子类的步骤

模板方法模式的钩子方法
1.在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为“钩子”。
2.还是用上面做豆浆的例子来讲解,比如,我们还希望制作纯豆浆,不添加任何的配料,请使用钩子方法对前面的模板方法进行改造

模板方法模式的注意事项和细节
1.基本思想是:算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类的模板芳法或者已经实现的某些步骤,子类就会继承这些修改
2.实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用。
3.既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现。
4.该模式的不足之处:每一个不同的实现都需要一个子类实现,导致类的个数增加,使得系统更加庞大
5.一般模板方法都加上final关键字,防止子类重写模板方法.
6.模板方法模式使用场景:当要完成在某个过程,该过程要执行一系列步骤,这一系列的步骤基本相同,但其个别步骤在实现时可能不同,通常考虑用模板方法模式来处理


命令模式
看一个具体的需求
1.我们买了一套智能家电,有照明灯、风扇、冰箱、洗衣机,我们只要在手机上安装app就可以控制对这些家电工作。
2.这些智能家电来自不同的厂家,我们不想针对每一种家电都安装一个App,分别控制,我们希望只要一个app就可以控制全部智能家电。
3.要实现一个app控制所有智能家电的需要,则每个智能家电厂家都要提供一个统一的接口给app调用,这时就可以考虑使用命令模式。
4.命令模式可将“动作的请求者”从“动作的执行者”对象中解耦出来.
5.在我们的例子中,动作的请求者是手机app,动作的执行者是每个厂商的一个家电产品

基本介绍
1.命令模式(Command Pattern):在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,
  也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计
2.命名模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
3.在命名模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命名),同时命令模式也支持可撤销的操作
4.通俗易懂的理解:将军发布命令,士兵去执行。其中有几个角色:将军(命令发布者)、士兵(命令的具体执行者)、命令(连接将军和士兵)。
  Invoker是调用者(将军),Receiver是被调用者(士兵),MyCommand是命令,实现了Command接口,持有接收对象

对原理类图的说明-即(命名模式的角色及职责)
1.Invoker是调用者角色
2.Command:是命令角色,需要执行的所有命令都在这里,可以是接口或抽象类
3.Receiver:接受者角色,知道如何实施和执行一个请求相关的操作
4.ConcreteCommand:将一个接受者对象与一个动作绑定,调用接受者相应的操作,实现execute

命令模式的注意事项和细节
1.将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的execute()方法就可以让接收者工作,
   而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说:”请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,
   命令对象起到了纽带桥梁的作用。
2.容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令
3.容易实现对请求的撤销和重做
4.命令模式不足:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这点在在使用的时候要注意
5.空命令也是一种设计模式,它为我们省去了判空的操作。在上面的实例中,如果没有用空命令,我们每按下一个按键都要判空,这给我们编码带来一定的麻烦。
6.命令模式经典的应用场景:界面的一个按钮都是一条命令、模拟CMD(DOS命令)订单的澈销/恢复、触发-反馈机制

访问者模式
完成测评系统需求
将观众分为男人和女人,对歌手进行测评,当看完某个歌手表演后,得到他们对该歌手不同的评价(评价有不同的种类,比如我功、失败等)
传统方式的问题分析
1.如果系统比较小,还是ok的,但是考虑系统增加越来越多新的功能时,对代码改动较大,违反了ocp原则,不利于维护
2.扩展性不好,比如增加了新的人员类型,或者管理方法,都不好做

访问者模式基本介绍
1.访问者模式(Visitor Pattern),封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
2.主要将数据结构与数据操作分离,解决数据结构和操作耦合性问题
3.访问者模式的基本工作原理是:在被访问的类里面加一个对外提供接待访问者的接口
4.访问者模式主要应用场景是:需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),
  同时需要避免让这些操作"污染"这些对象的类,可以选用访问者模式解决

对原理类图的说明-即(访问者模式的角色及职责)
1) visitor是抽象访问者,为该对象结构中的Concret eElement的每一个类声明一个visit操作
2) Concreteisitor:是一个具体的访问值实现每个有Visitor声明的操作,是每个操作实现的部分
3) 0bjectStructure能枚举它的元素,可以提供一个高层的接口,用来允许访问者访问元素
4) Element定义一个accept方法,接收一个访问者对象
5) ConcreteElenent为具体元素,实现了accept方法

4)应用案例的小结
-上面提到了双分派,所谓双分派是指不管类怎么变化,我们都能找到期望的方法运行双分派意味着得到执行的操作取决于请求的种类和两个接收者的类型
-以上述实例为例,假设我们要添加一个Wait的状态类,考察Man类和Woman类的反应,由于使用了双分派,只需增加一个Action子类即可在客户端调用即可,
不需要改动任何其他类的代码。

访问者模式的注意事项和细节
优点
1)访问者模式符合单一职责原则、让程序具有优秀的扩展性、灵活性非常高
2)访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统
≥缺点
1)具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的,这样造成了具体元素变更比较困难
2)违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素
3)因此,如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的.

迭代器模式
看一个学校院系展示需求
编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系。
如图:
-------清华大学--------
-------计算机学院-
计算机科学与技术
软件工程
网络工程
-------信息工程学院---------
通信工程
信息工程

传统的方式的问题分析
1)将学院看做是学校的子类,系是学院的子类,这样实际上是站在组织大小来进行分层次的
2)实际上我们的要求是:在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系,因此这种方案,不能很好实现的遍历的换作
3)解决方案:=→迭代器模式

基本介绍
1)迭代器模式( Iterator Pattern)是常用的设计模式,属任行为型模式
2)如果我们的集合元素是用不同的方式实现的,有数组,还有java的集合类,或者还有其他方式,当客户端要遍历这些集合元素的时候就要使用多种遍历方式,
  而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决。
3)迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即:不暴露其内部的结构。

对原理类图的说明-即迭代器模式的角色及职责
1.Iterator:迭代器接口,是系统提供,含义 hasNext, next,remove
2.ConcreteIterator :具体的迭代器类,管理迭代
3.Aggregate :一个统一的聚合接口,将客户端和具体聚合解耦
4.ConcreteAggregate :具体的聚合持有对象集合,并提供一个方法,返回一个迭代器,该迭代器可以正确遍历集合
5.Client :客户端,通过Iterator和Aggregate依赖子类

迭代器模式的注意事项和细节
>优点
1)提供一个统一的方法遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以遍历对象了。
2)隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器,而不会知道聚合的具体组成。
3) 提供了一种设计思想,就是一个类应该只有一个引起变化的原因(叫做单一责任原则)。在聚合类中,我们把迭代器分开,
   就是要把管理对象集合和遍历对象集合的责任分开,这样一来集合改变的话,只影响到聚合对象。而如果遍历方式改变的话,只影响到了迭代器。
4)当要展示一组相似对象,或者遍历一组相同对象时使用,适合使用迭代器模式
>缺点
每个聚合对象都要一个迭代器,会生成多个迭代器不好管理类


观察者模式
天气预报项目需求,具体要求如下:
1.气象站可以将每夹测量到的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方)。
2.需要设计开放型API,便于其他第三方也能接入气象站获取数据。
3.提供温度、气压和湿度的接口
4.测量数据更新时,要能实时的通知给第三方

天气预报设计方案1-普通方案
WeatherData类
通过对气象站项目的分析,我们可以初步设计出一个WeatherData类

说明:
1.通过getXxx方法,可以让第三方接入,并得到相关信息.
2.当数据有更新时,气象站通过调用dataChange()去更新数据,当第三方再次获取时,就能得到最新数据,当然也可以推送。
3.违反了ocp原则=>观察者模式

>问题分析
1.其他第三方接入气象站获取数据的问题
2.无法在运行时动态的添加第三方
在WeatherData中,当增加一个第三方,都需要创建一个对应的第三方的公告板对象,并加入到dataChange,不利于维护,也不是动态加入
public void dataChange() {
currentConditions.update(getTemperature(), getPressure(), getHumidity());}

观察者模式类似订牛奶业务
1)奶站/气象局:Subject
2)用户/第三方网站:Observer
Subject:登记注册、移除和通知
1.registerObserver注册
2.removeObserver移除
3.notifyObservers()通知所有的注册的用户,根据不同需求,可以是更新数据,让用户来取,也可能是实施推送,看具体需求定

观察者模式原理
Observer: 接收输入
观察者模式:对象之间多对一依赖的一种设计方案,被依赖的对象为Subject,依赖的对象为Observer,subject通知Observer变化,
          比如这里的奶站是subject,是1的一方。用户时Observer,是多的一方。

观察者模式的好处
1.观察者模式设计后,会以集合的方式来管理用户(Observer),包括注册,移除和通知。
2.这样,我们增加观察者(这里可以理解成一个新的公告板),就不需要去修改核心类WeatherData不会修改代码,遵守了ocp原则。

中介者模式
智能家庭项目:
1.智能家庭包括各种设备,闹钟、咖啡机、电视机、窗帘等
2.主人要看电视时,各个设备可以协同工作,自动完成看电视的准备工作,比如流程为:闹铃响起->咖啡机开始做咖啡->窗帘自动落下->电视机开始播放

传统的方式的问题分析
1.当各电器对象有多种状态改变时,相互之间的调用关系会比较复杂
2.各个电器对象彼此联系,你中有我,我中有你,不利于松耦合.
3.各个电器对象之间所传递的消息(参数),容易混乱
4.当系统增加一个新的电器对象时,或者执行流程改变时,代码的可维护性、扩展性都不理想→考虑中介者模式

基本介绍
1.中介者模式(Mediator Pattern),用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互刳用,
  从而使其耦合松散,而且可以独立地改变它们之间的交互
2.中介者模式属于行为型模式,使代码易于维护
3.比如MVc模式,c (Controller控制器)是M(Model模型)和v (View视图)的中介者,在前后端交互时起到了中间人的作用


对原理类图的说明-即(中介者模式的角色及职麦)
1.Mediator就是抽象中介者,定义了同事对象到中介者对象的接口
2.Colleague是抽象同事类
3.ConcreteMediator具体的中介者对象,实现抽象方法,他需要知道所有的具体的同事类,即以一个集合来管理HashMap,并接受某个同事对象消息,完成相应的任务
4.ConcreteColleague具体的同事类,会有很多,每个同事只知道自己的行为,而不了解其他同事类的行为(方法),但是他们都依赖中介者对象

中介者模式-智能家庭的操作流程
1.创建ConcreMediator对象
2.创建各个同事类对象,比如:Alarm,CoffeeMachine,TV..
3.在创建同事类对象的时候,就直接通过构造器,加入到colleagueMap
4.同事类对象,可以调用sendMessage ,最终会去调用ConcreteMediator的getMessage方法
5.getMessage会根据接收到的同事对象发出的消息来协调调用其它的同事对象,完成任务
6.可以看到getMessage是核心方法,完成相应任务

中介者模式的注意事项和细节
1)多个类相互耦合,会形成网状结构,使用中介者模式将网状结构分离为星型结构,进行解耦
2)减少类间依赖,降低了耦合,符合迪米特原则
3)中介者承担了较多的责任,一旦中介者出现了问题,整个系统就会受到影响
4)如果设计不当,中介者对象本身变得过于复杂,这点在实际使用时,要特别注意


备忘录模式
游戏角色状态恢复问题
游戏角色有攻击力和防御力,在大战Boss前保存自身的状态(攻击力和防御力),当大战Boss后攻击力和防御力下降,从备忘录对象恢复到大战前的状态

传统的方式的问题分析
1.一个对象,就对应一个保存对象状态的对象,这样当我们游戏的对象很多时,不利于管理,开销也很大.
2.传统的方式是简单地做备份,new出另外一个对象出来,再把需要备份的数据放到这个新对象,但这就暴露了对象内部的细节
3.解决方案:=>备忘录模式

基本介绍
1.备忘录模式(Memento Pattern)在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态
2.可以这里理解备忘录模式:现实生活中的备忘录是用来记录某些要去做的事情,或者是记录已经达成的共同意见的事情,以防忘记了。而在软件层面,
  备忘录模式有着相同的含义,备忘录对象主要用来记录一个对象的某种状态,或者某些数据,当要做回退时,可以从备忘录对象里获取原来的数据进行恢复操作
3.备忘录模式属于行为型模式

对原理类图的说明-即(备忘录模式的角色及职责)
1.originator :对象(需要保存状态的对象)
2.Memento :备忘录对象,负责保存好记录,即Originator内部状态
3.Caretaker:守护者对象,负责保存多个备忘录对象,使用集合管理,提高效率
4.说明:如果希望保存多个originator对象的不同时间的状态,也可以,只需要使用HashMap<String,集合>

备忘录模式的注意事项和细节
1.给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态
2.实现了信息的封装,使得用户不需要关心状态的保存细节
3.如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存,这个需要注意
4.适用的应用场景:1、后悔药。2、打游戏时的存档。3、Windows里的ctr+z。4、IE中的后退。5、数据库的事务管理
5.为了节约内存,备忘录模式可以和原型模式配合使用


解释器模式
通过解释器模式来实现四则运算,如计算a+b-c的值,具体要求
1)先输入表达式的形式,比如a+b+c-d+e,要求表达式的字母不能重复
2)在分别输入a ,b, c, d, e的值
3)最后求出结果:如图
请谕入表达式:a+b+c-d+e
请输入a的值:10
请输入b的值:11
请输入c的值:1
请输入d的值:2
请输入e的值:3
运算结果: a+b+c-d+e=23

传统方案解决四则运算问题分析
1.编写一个方法,接收麦达式的形式,然后根据用户输入的数值进行解析,得到结果
2.问题分析:如果加入新的运算符,比如*/(等等,不利于扩展,另外让一个方法来解析会造成程序结构混乱,不够清晰.
3.解决方案:可以考虑使用解释器模式,即:表达式→>解释器(可以有多种)→>结果

基本介绍
1.在编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元再通过语法分析器构建语法分析树,
  最终形成一颗抽象的语法分析树。这里的词法分析器和语法分析器都可以看做是解释器
2.解释器模式( Interpreter Pattern):是指给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)
3.应用场景: 应用可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
          一些重复出现的问题可以用一种简单的语言来表达
          一个简单语法需要解释的场景
4.这样的例子还有,比如编译器、运算表达式计算、正则表达式、机器人等

对原理类图的说明-即(解释器模式的角色及职责)
1.Context:是环境角色,含有解释器之外的全局信息.
2.AbstractExpression:抽象表达式,声明一个抽象的解释操作,这个方法为抽象语法树中所有的节点所共享
3.TerminalExpression:为终结符表达式。实现与文法中的终结符相关的解释操作
4.NonTerminalExpression:为非终结符表达式,为文法中的非终结符实现解释操作.
5.说明:输入Context 和 TerminalExpression信息通过client输入即可

解释器模式的注意事项和细节
1.当有一个语言需要解释执行,可将该语言中的句子表示为一个抽象语法树)就可以考虑使用解释器模式,让程序具有良好的扩展性
2.应用场景:编译器、运算表达式讦算、正则表达式、机器人等
3.使用解释器可能带来的问题:解释器模式会引起类膨胀、解释器模式采用递归调用方法,将会导致调试非常复杂、效率可能降低.


状态模式
请编写程序完成APP抽奖活动具体要求如下:
1)假如每参加一次这个活动要扣除用户50积分,中奖概率是10%
2)奖品数量固定,抽完就不能抽奖
3)活动有四个状态:可以抽奖、不能抽奖、发放奖品和奖品领完
4)活动的四个状态转换关系图(右图) ztms.png

基本介绍
1.状态模式(State Pattern) :它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换
2.当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类

对原理类图的说明-即(状态模式的角色及职责)
1.Context类为环境角色。用于维护State实例,这个实例定义当前状态
2.State是抽象状态角色定义一个接口封装与Cantext的一个特点接口相关行为
3.ConcreteState具体的状态角色,每个子类实现一个与Cortext 的一个状态相关行为

状态模式的注意事项和细节
1.代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中
2.方便维护。将容易产生问题的if-else语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,
  不但会产出很多if-else语句,而且容易出错
3.符合“开闭原则”。容易增删状态
4.会产生很多类。每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度
5.应用场景: 当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候,可以考虑使用状态模式

策略模式
编写鸭子项目,具体要求如下:
1)有各种鸭子(比如野鸭、北京鸭、水鸭等,鸭子有各种行为,比如叫、飞行等)
2)显示鸭子的信息

传统的方式实现的问题分析和解决方案
1.其它鸭子,都继承了Duck类,所以fly让所有子类都会飞了,这是不正确的
2.上面说的1的问题,其实是继承带来的问题:对类的局部改动,尤其超类的局部改动,会影响其他部分。会有溢出效应
3.为了改进1问题,我们可以通过覆盖fly方法来解决=>覆盖解决
4.问题又来了,如果我们有一个玩具鸭子ToyDuck,这样就需要ToyDuck去覆盖Duck的所有实现的方法=>解决思路策略模式

基本介绍
1)策略模式(Strategy Pattern)中,定义算法族(策略组),分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
2)这算法体现了几个设计原则:
第一、把变化的代码从不变的代码中分离出来;
第二、针对接口编程而不是具体类(定义了策略接口)﹔
第三、多用组合/聚合,少用继承(客户通过组合方式使用策略)。

说明:从上图可以看到,客户context有成员变量strategy或者其他的策略接口,至于需要使用到哪个策略,我们可以在构造器中指定.

策略模式的注意事项和细节
1.策略模式的关键是:分析项目中变化部分与不变部分
2.策略模式的核心思想是:多用组合/聚合少用继承;用行为类组合,而不是行为的继承。更有弹性
3.体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行为)即可,避免了使用多重转移语句(if..else if..else)
4.提供了可以替换继承关系的办法:策略模式将算法封装在独立的Strategy类中使得你可以独立于其Context改变它,使它易于切换、易于理解、易于扩展
5.需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞大


职责链模式

OA系统采购审批需求
学校OA系统的采购审批项目:需求是
1.采购员采购教学器材
2.如果金额小于等于5000,由教学主任审批
3.如果金额小于等于10000,由院长审批
4.如果金额小于等于30000,由副校长审批
5.如果金额超过30000以上,由校长审批
请设计程序完成采购审批项目

传统方案解决oA系统审批问题分析
1.传统方式是:接收到一个采购请求后,根据采购金额来调用对应的Approver(审批人)完成审批。
2.传统方式的问题分析:客户端这里会使用到分支判断(比如 switch)来对不同的采购请求处理,这样就存在如下问题
  (1)如果各个级别的人员审批金额发生变化,在客户端的也需要变化
  (2)客户端必须明确的知道有多少个审批级别和访问
3.这样对一个采购请求进行处理和Approver(审批人)就存在强耦合关系,不利于代码的扩展和维护
4.解决方案=》职责链模式

基本介绍
1.职责链模式(Chain of Responsibility Pattern) ,又叫责任链模式,为请求创建了一个接收者对象的链。这种模式对请求的发送者和接收者进行解耦。
2.职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
3.这种类型的设计模式属于行为型模式

对原理类图的说明-即(职责链模式的角色及职责)
职责链模式(Chain Of Responsibility) ,使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。
将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止.
1.Handler :抽象的处理者,定义了一个处理请求的接口,同时含义另外Handler
2.ConcreteiandlerA , B是具体的处理者,处理它自己负责的请求,可以访问它的后继者(即下一个处理者),
  如果可以处理当前请求,则处理,否则就将该请求交个后继者去处理,从而形成一个职责链3)Request ,含义很多属性,表示一个请求


职责链模式的注意事项和细节
1.将请求和处理分开,实现解耦,提高系统的灵活性
2.简化了对象,使对象不需要知道链的结构
3.性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在Handler中设置一个最大节点数量,
  在setNext()方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能
4.调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂
5.最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪等审批流程、Java Web中Tomcat对Encoding的处理、拦截器
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值