Coder Social home page Coder Social logo

designpattern's Introduction

设计模式

##原则

  1. 开闭原则(Open Close Principle)
 开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。
 所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后
 面的具体设计中我们会提到这点。
  1. 里氏代换原则(Liskov Substitution Principle)
 里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基
 类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到
 影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。
 实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现
 抽象化的具体步骤的规范。
  1. 依赖倒转原则(Dependence Inversion Principle)
 这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。
  1. 接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,
其实设计模式就是一个软件的设计**,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。
  1. 迪米特法则(最少知道原则)(Demeter Principle)
为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
  1. 合成复用原则(Composite Reuse Principle)
原则是尽量使用合成/聚合的方式,而不是使用继承。

##总览 启动界面

##A创建型 ###1单例模式

确保一个类只有一个实例,并提供一个全局访问点.

使用场景
例如线程池,缓存,日志对象,驱动程序等,我们只需要一个对象.(用java全局变量比如静态变量也可以实现,但是如果一个对象非常消耗资源,
程序一开始就要初始化它,而后程序执行过程中,可能没有用.)
适用
当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。

###2工厂模式

####2.1工厂方法模式(子厂你自由发挥吧)

定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类

使用场景
 1日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
 2数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
 3设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。
适用
当一个类不知道它所必须创建的对象的类的时候。

当一个类希望由它的子类来指定它所创建的对象的时候。

当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。

####2.2抽象工厂模式(我要开不一样的分厂)

提供一个创建一系列相关或者相互依赖的对象接口,而无需指定它们具体的类

 使用场景
 一个系统要独立于它的产品的创建、组合和表示时。

 一个系统要由多个产品系列中的一个来配置时。

 当你要强调一系列相关的产品对象的设计以便进行联合使用时。

 当你提供一个产品类库,而只想显示它们的接口而不是实现时。

 0. 工厂方法分简单工厂(事实上不是23种设计模式),多个工厂方法模式和静态工厂,在普通工厂方法模式中,如果传递的字符串出错,则不
 能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。静态工厂模式,把多个工厂方法模式里的方法置为静态的,不需
 要创建实例,直接调用即可
 1. 工厂用来处理创建对象的细节.解决的问题是,对象的初始化经常造成耦合,工厂模式主要处理复杂的依赖.
 2. 工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则.抽象
 工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码.

 3.工厂方法和抽象工厂的区别
 3.1工厂方法使用继承,把对象的创建委托给子类,子类实现工厂方法来创建对象.抽象工厂使用对象组合,对象的创建被实现在工厂接口暴露
  出来的方法中
 4.工厂方法和抽象工厂的优缺点
 4.1工厂方法
 4.1.1优点,易于添加新的产品,相比较简单工厂符合开-闭原则.
 4.1.2缺点,新产品的添加,需要新建大量的类,客户端部分仍然违反开闭原则
 4.2抽象工厂
 4.2.1优点,分离了具体的类,工厂封装了创建产品对象的责任和过程,将客户端和类的实现分离,通过抽象接口操纵实例
          易于交换产品系列,一个具体的工厂类在一个应用中只有初始化时出现.
          有利于产品的一致性,一个系列的产品中的产品对象被设计成一起工作时,一个应用只能同时使用同一系列的对象
 4.2.2缺点,难以支持新种类的产品,抽象工厂接口确定了可以被创建的产品集合,新种类的产品加入需要扩展抽象工厂接口,这就涉及所有实现
          类的改变
 反射技术可以改变客户端违反开闭原则的问题.

###建造者模式(国王**下的繁荣)

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

 适用

 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
 当构造过程必须允许被构造的对象有不同的表示时。
 与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。

 适用场景
 1、去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。
 2、JAVA 中的 StringBuilder。
 3.一些基本部件不会变,而其组合经常变化的时候。

 建造者:创建和提供实例,导演:管理建造出来的实例的依赖关系。(可以理解为导演指导演员去演戏,
 导演管理着演员,演员去演戏)

###原型模式(clone)

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

应用场景
1、细胞分裂。 2、JAVA 中的 Object clone() 方法。

适用性:
当要实例化的类是在运行时刻指定时,例如,通过动态装载;或者

为了避免创建一个与产品类层次平行的工厂类层次时;或者

当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。

所谓原型模式,就是依托一个已经实例化的对象去创建另外一个可以进行定制的对象,而不需要知道创建过程中的具体细节

原型模式的优点
使用原型模式创建对象比直接new一个对象在性能上要好的多,因为Object类的clone方法是一个本地方法,
它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。
使用原型模式的另一个好处是简化对象的创建,使得创建对象就像我们在编辑文档时的复制粘贴一样简单。
因为以上优点,所以在需要重复地创建相似对象时可以考虑使用原型模式。比如需要在一个循环体内创建对象,
假如对象创建过程比较复杂或者循环次数很多的话,使用原型模式不但可以简化创建过程,而且可以使系统的整体性能提高很多。

使用原型模式复制对象不会调用类的构造方法。因为对象的复制是通过调用Object类的clone方法来完成的,它直接在内存中复制数据, 因此不会调用到类的构造方法。不但构造方法中的代码不会执行,甚至连访问权限都对原型模式无效。还记得单例模式吗?单例模式中, 只要将构造方法的访问权限设置为private型,就可以实现单例。但是clone方法直接无视构造方法的权限,所以,单例模式与原型模式是冲突的 ,在使用时要特别注意。 深拷贝与浅拷贝。Object类的clone方法只会拷贝对象中的基本的数据类型,对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。 如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。

##B结构型

###桥接模式(桥的两头,一边是抽象,一边是变化)

将抽象部分与它的实现部分分离,使它们都可以独立地变化。

 适用性:

 你不希望在抽象和它的实现部分之间有一个固定的绑定关系。例如这种情况可能是因为,在程序运行时刻实现部分应可以被选择或者切换。

 类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充。这时Bridge 模式使你可以对不同的抽象接口和实现部分进行组合,
 并分别对它们进行扩充。

 对一个抽象的实现部分的修改应对客户不产生影响,即客户的代码不必重新编译。

组合与聚合

    尽量使用组合与聚合,不要使用继承.
    1)聚合是关联关系的一种特例,他体现的是整体与部分、拥有的关系,即has-a的关系,此时整体与部分之间是可分离的,
    他们可以具有各自的生命周期,部分可 以属于多个整体对象,也可以为多个整体对象共享;比如计算机与CPU、公司与员工的关系等;
    表现在代码层面,和关联关系是一致的,只能从语义级别来区分。
    Public class A{
       Private B b;
       public set(B b){
       This.b=b;
       }
    }
    2)组合也是关联关系的一种特例,他体现的是一种contains-a的关系,这种关系比聚合更强,也称为强聚合;他同样体现整体与部分间的关系,
    但此时整体 与部分是不可分的,整体的生命周期结束也就意味着部分的生命周期结束;比如你和你的大脑;表现在代码层面,和关联关系是
    一致的,只能从语义级别来区分。
      Public class A{
        Private B b = new B(); //第一种方法;
        Public A(){ b=new B(); } //第二种方法;
        Public A(B b){ This.b=b; } //第三种方法;
      }
    3)依赖关系可以简单的理解,就是一个类A使用到了另一个类B,而这种使用关系是具有偶然性的、临时性的、非常弱的,
    但是B类的变化会影响到A;比如某人要过河,需要借用一条船,此时人与船之间的关系就是依赖;表现在代码层面,为类B作
    为参数被类A在某个method方法中使用Public class A {//注意这没有定义类B在类A中的私有变量
      public B operation(B b){//返回值和方法参数
              b.opb();//方法参数位置
              B bb = new B();//局部变量
              bb.opbb();
              B.staticop();//静态方法调用
      }
      }
     4)关联体现的是两个类、或者类与接口之间语义级别的一种强依赖关系,比如我和我的朋友;这种关系比依赖更强、
     不存在依赖关系的偶然性、关系 也不是临时性的,一般是长期性的,而且双方的关系一般是平等的、关联可以是单向、
     双向的;表现在代码层面,为被关联类B以类属性的形式出现在关联类A中, 也可能是关联类A引用了一个类型为被关联
     类B的全局变量.所谓关联就是某个对象会长期的持有另一个对象的引用,而二者的关联往往也是相互的。关联的两个对象
     彼此间没有任何强制性的约束,只要二者同意,可以随时解除关系或是进行关联,它们在生命期问题上没有任何约定。
     被关联的对象还可以再被别的对象关联,所以关联是可以共享的。
     Public class A{
         Private B b;
       }

桥连接模式的抽象与实现分离,实现是指抽象类和派生类用来实现自己的对象,桥连接适用于多重分类并且每种分类都需要独立变化. 把当前要实现的对象,传到abstraction中,去调用他的方法,至于要更换新的对象新的方法,只需要更换RedinedAbstraction.

###适配器模式(你要的size,我给你改)

将一个类的接口转换成客户希望的另一个接口

主要用在系统的后期维护,和第三方组件与系统接口不同.

应用场景
1、美国电器 110V,** 220V,就要有一个适配器将 110V 转化为 220V。
2、JAVA JDK 1.1 提供了 Enumeration 接口,而在 1.2 中提供了 Iterator 接口,想要使用 1.2 的 JDK,
则要将以前系统的 Enumeration 接口转化为 Iterator 接口,这时就需要适配器模式。
3、在 LINUX 上运行 WINDOWS 程序。
4、JAVA 中的 jdbc。

优点: 1、可以让任何两个没有关联的类一起运行。 2、提高了类的复用。 3、增加了类的透明度。 4、灵活性好。
缺点: 1、过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,
其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,
可以不使用适配器,而是直接对系统进行重构。
2.由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。

###装饰模式(变变变)

动态的给对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活.(子类方式无法在程序运行中添加职责)

jdk中的I/O用的就是装饰者模式

###组合模式(组织结构树)

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

   优点: 1、高层模块调用简单。 2、节点自由增加。
   缺点:在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。
   使用场景:部分、整体场景,如树形菜单,文件、文件夹的管理。
   注意事项:定义时为具体类。

###享元模式(拒绝浪费,学会分享)

运用共享技术有效地支持大量细粒度的对象。系统只使用少量的对象,而这些对象都很相似,状态变化很小, 可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。

适用性:

一个应用程序使用了大量的对象。

完全由于使用大量的对象,造成很大的存储开销。

对象的大多数状态都可变为外部状态。

如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。

应用程序不依赖于对象标识。由于Flyweight 对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值。

###外观模式(统一外观)

为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

适用性:

当你要为一个复杂子系统提供一个简单接口时。子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生
更多更小的类。这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一
些使用上的困难。Facade 可以提供一个简单的缺省视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制性
的用户可以越过facade层。

客户程序与抽象类的实现部分之间存在着很大的依赖性。引入facade 将这个子系统与客户以及其他的子系统分离,可以提
高子系统的独立性和可移植性。

当你需要构建一个层次结构的子系统时,使用facade模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,你可
以让它们仅通过facade进行通讯,从而简化了它们之间的依赖关系。

###代理模式(翻墙)

给某一个对象提供一个代理,并由代理对象控制对原对象的引用。

 在需要用比较通用和复杂的对象指针代替简单的指针的时候,使用Proxy模式。下面是一 些可以使用Proxy 模式常见情况:
 1) 远程代理(Remote Proxy )为一个对象在不同的地址空间提供局部代表。 NEXTSTEP[Add94] 使用NXProxy 类
 实现了这一目的。Coplien[Cop92] 称这种代理为“大使” (Ambassador )。
 2 )虚代理(Virtual Proxy )根据需要创建开销很大的对象。在动机一节描述的ImageProxy 就是这样一种代理的例子。
 3) 保护代理(Protection Proxy )控制对原始对象的访问。保护代理用于对象应该有不同 的访问权限的时候。例如,
 在Choices 操作系统[ CIRM93]中KemelProxies为操作系统对象提供 了访问保护。
 4 )智能指引(Smart Reference )取代了简单的指针,它在访问对象时执行一些附加操作。 它的典型用途包括:

 对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放它(也称为SmartPointers[Ede92 ] )。

 当第一次引用一个持久对象时,将它装入内存。

 在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。

##C行为型

###模板方法(量产)

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。TemplateMethod 使得子类可以不改变一个算法的结构 即可重定义该算法的某些特定步骤。

一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
当子类有公共的行为可以提取到父类中,并且子类有自己的个性化要求时
一般子类需要扩展的地方是固定的,即仅允许在这些点进行扩展

###策略模式(变化)

定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。(策略模式封装了变化)

 策略可以理解为算法,他们完成相同的工作,只是处理过程不尽相同,也就是说相同的输入,由于策略不同会有不同的输出
 策略模式以相同的调用来使用所有算法,降低了算法类和使用算法类的耦合.
 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
 一个类定义了多种行为, 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各
 自的Strategy类中以代替这些条件语句。


 适用于
 类只是行为上有差异
 需要使用一个算法的不同变体时
 使用算法的客户不应该知道算法的具体结构时
 一个类定义了多种行为, 并且这些行为在这个类的操作中以多个条件语句的形式出现。

###状态模式(改变状态)

允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。

   当一个对象取决于它的状态并且在运行状态中根据状态改变自己的行为
   当一个操作中有大量的分支条件语句,并且这些分支都依赖于该对象的状态.

###观察者模式(订阅)

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。

 适用性:

 当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。

 当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。

 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。

jdk中的Observable类和Observer接口,分别为主题,和观察者

###备忘录模式(数据备份)

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

    适用性:

    必须保存一个对象在某一个时刻的(部分)状态, 这样以后需要时它才能恢复到先前的状态。

    如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。

###中介者 (客服,有问题问技术人员)

用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

适用性:

一组对象以定义良好但是复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解。

一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。

想定制一个分布在多个类中的行为,而又不想生成太多的子类。

###命令模式(这是一个命令,你去做吧)

将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。

适用
当需要抽象出待执行的动作以便参数化某个对象时
当需要在不同时刻执行,排列以及执行请求时
当系统需要取消操作时
当系统需要修改记录日志

命令模式的本质是对命令进行封装,并且将发出命令的责任和执行命令的责任分割开 请求的一方发出请求,接收方执行请求,命令模式使请求变成了一个对象,关键在于抽象命令接口,发送者根据此进行编程 只有具体命令才会与接受者相互关联

 优点
 新的命令很容易加入xitong
 很容易设计一个命令队列或组合命令
 低耦合
 何以撤销和重做

###责任链模式

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链, 并沿着这条链传递该请求,直到有一个对象处理它为止。

单一职责

适用性:

有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。

你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。

可处理一个请求的对象集合应被动态指定。

优点
降低耦合度
简化对象的相互连接
加强对象指派责任的灵活性
增加对象请求处理非常方便

缺点
不能保证请求一定会被接受
性能受影响
可能造成循环调用

###迭代器模式

提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。

 适用性:

 访问一个聚合对象的内容而无需暴露它的内部表示。

 支持对聚合对象的多种遍历。

 为遍历不同的聚合结构提供一个统一的接口(即, 支持多态迭代)。

###解释器

给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

 当有一个语言需要解释执行, 并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。
 而当存在以下情况时该模式效果最好:

 该文法简单对于复杂的文法, 文法的类层次变得庞大而无法管理。此时语法分析程序生成器这样的工具是更好的选择。
 它们无需构建抽象语法树即可解释表达式, 这样可以节省空间而且还可能节省时间。

 效率不是一个关键问题最高效的解释器通常不是通过直接解释语法分析树实现的, 而是首先将它们转换成另一种形式。
 例如,正则表达式通常被转换成状态机。但即使在这种情况下, 转换器仍可用解释器模式实现, 该模式仍是有用的。

   解释器是一个简单的语法分析工具,它最显著的优点就是扩展性,修改语法规则只需要修改相应的非终结符就可以了,
   若扩展语法,只需要增加非终结符类就可以了。
   但是,解释器模式会引起类的膨胀,每个语法都需要产生一个非终结符表达式,语法规则比较复杂时,
   就可能产生大量的类文件,为维护带来非常多的麻烦。同时,由于采用递归调用方法,每个非终结符表达式只关心与
   自己相关的表达式,每个表达式需要知道最终的结果,必须通过递归方式,无论是面向对象的语言还是面向过程的语言,
   递归都是一个不推荐的方式。由于使用了大量的循环和递归,效率是一个不容忽视的问题。
   特别是用于解释一个解析复杂、冗长的语法时,效率低

designpattern's People

Contributors

helmeter avatar

Watchers

James Cloos avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.