-
2018-05-10 10:34:34
个人Github-24种设计模式案例链接
创建型模式
工厂模式
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
介绍
意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
主要解决:主要解决接口选择的问题。
何时使用:我们明确地计划不同条件下创建不同实例时。
如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。
关键代码:创建过程在其子类执行。
应用实例: 1、您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。 2、Hibernate 换数据库只需换方言和驱动就可以。
优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。
缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
使用场景: 1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。 2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。 3、设计一个连接服务器的框架,需要三个协议,”POP3”、”IMAP”、”HTTP”,可以把这三个作为产品类,共同实现一个接口。
注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
抽象工厂模式
抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
介绍
意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
主要解决:主要解决接口选择的问题。
何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
如何解决:在一个产品族里面,定义多个产品。
关键代码:在一个工厂里聚合多个同类产品。
应用实例:工作了,为了参加一些聚会,肯定有两套或多套衣服吧,比如说有商务装(成套,一系列具体产品)、时尚装(成套,一系列具体产品),甚至对于一个家庭来说,可能有商务女装、商务男装、时尚女装、时尚男装,这些也都是成套的,即一系列具体产品。假设一种情况(现实中是不存在的,要不然,没法进入共产主义了,但有利于说明抽象工厂模式),在您的家中,某一个衣柜(具体工厂)只能存放某一种这样的衣服(成套,一系列具体产品),每次拿这种成套的衣服时也自然要从这个衣柜中取出了。用 OO 的思想去理解,所有的衣柜(具体工厂)都是衣柜类的(抽象工厂)某一个,而每一件成套的衣服又包括具体的上衣(某一具体产品),裤子(某一具体产品),这些具体的上衣其实也都是上衣(抽象产品),具体的裤子也都是裤子(另一个抽象产品)。
优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。
使用场景: 1、QQ 换皮肤,一整套一起换。 2、生成不同操作系统的程序。
注意事项:产品族难扩展,产品等级易扩展。
单例模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。介绍
意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候。
如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
关键代码:构造函数是私有的。
应用实例: 1、一个党只能有一个主席。 2、Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。 3、一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。
优点: 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。 2、避免对资源的多重占用(比如写文件操作)。
缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
使用场景: 1、要求生产唯一序列号。 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
注意事项:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。
建造者模(构建者模式)
建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。
介绍
意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
主要解决:主要解决在软件系统中,有时候面临着”一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
何时使用:一些基本部件不会变,而其组合经常变化的时候。
如何解决:将变与不变分离开。
关键代码:建造者:创建和提供实例,导演:管理建造出来的实例的依赖关系。
应用实例: 1、去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的”套餐”。 2、JAVA 中的 StringBuilder。
优点: 1、建造者独立,易扩展。 2、便于控制细节风险。
缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。
使用场景: 1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖。
注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。
原型模式
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
介绍
意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
主要解决:在运行期建立和删除原型。
何时使用: 1、当一个系统应该独立于它的产品创建,构成和表示时。 2、当要实例化的类是在运行时刻指定时,例如,通过动态装载。 3、为了避免创建一个与产品类层次平行的工厂类层次时。 4、当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
如何解决:利用已有的一个原型对象,快速地生成和原型对象一样的实例。
关键代码: 1、实现克隆操作,在 JAVA 继承 Cloneable,重写 clone(),在 .NET 中可以使用 Object 类的 MemberwiseClone() 方法来实现对象的浅拷贝或通过序列化的方式来实现深拷贝。 2、原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些”易变类”拥有稳定的接口。
应用实例: 1、细胞分裂。 2、JAVA 中的 Object clone() 方法。
优点: 1、性能提高。 2、逃避构造函数的约束。
缺点: 1、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。 2、必须实现 Cloneable 接口。
使用场景: 1、资源优化场景。 2、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。 3、性能和安全要求的场景。 4、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。 5、一个对象多个修改者的场景。 6、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。 7、在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用。
注意事项:与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流。
结构型模式
适配器模式
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。
这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。
我们通过下面的实例来演示适配器模式的使用。其中,音频播放器设备只能播放 mp3 文件,通过使用一个更高级的音频播放器来播放 vlc 和 mp4 文件。
介绍
意图:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
主要解决:主要解决在软件系统中,常常要将一些”现存的对象”放到新的环境中,而新环境要求的接口是现对象不能满足的。
何时使用: 1、系统需要使用现有的类,而此类的接口不符合系统的需要。 2、想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。 3、通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)
如何解决:继承或依赖(推荐)。
关键代码:适配器继承或依赖已有的对象,实现想要的目标接口。
应用实例: 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 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。
使用场景:有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。
注意事项:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。
装饰器模式
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
我们通过下面的实例来演示装饰器模式的用法。其中,我们将把一个形状装饰上不同的颜色,同时又不改变形状类。
介绍
意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。
何时使用:在不想增加很多子类的情况下扩展类。
如何解决:将具体功能职责划分,同时继承装饰者模式。
关键代码: 1、Component 类充当抽象角色,不应该具体实现。 2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。
应用实例: 1、孙悟空有 72 变,当他变成”庙宇”后,他的根本还是一只猴子,但是他又有了庙宇的功能。 2、不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。
优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
缺点:多层装饰比较复杂。
使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销。
注意事项:可代替继承。
代理模式
在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。
在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。
介绍
意图:为其他对象提供一种代理以控制对这个对象的访问。
主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
何时使用:想在访问一个类时做一些控制。
如何解决:增加中间层。
关键代码:实现与被代理类组合。
应用实例: 1、Windows 里面的快捷方式。 2、猪八戒去找高翠兰结果是孙悟空变的,可以这样理解:把高翠兰的外貌抽象出来,高翠兰本人和孙悟空都实现了这个接口,猪八戒访问高翠兰的时候看不出来这个是孙悟空,所以说孙悟空是高翠兰代理类。 3、买火车票不一定在火车站买,也可以去代售点。 4、一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制。 5、spring aop。
优点: 1、职责清晰。 2、高扩展性。 3、智能化。
缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
使用场景:按职责来划分,通常有以下使用场景: 1、远程代理。 2、虚拟代理。 3、Copy-on-Write 代理。 4、保护(Protect or Access)代理。 5、Cache代理。 6、防火墙(Firewall)代理。 7、同步化(Synchronization)代理。 8、智能引用(Smart Reference)代理。
注意事项: 1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。 2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
外观模式
外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。
这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用。
介绍
意图:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
主要解决:降低访问复杂系统的内部子系统时的复杂度,简化客户端与之的接口。
何时使用: 1、客户端不需要知道系统内部的复杂联系,整个系统只需提供一个”接待员”即可。 2、定义系统的入口。
如何解决:客户端不与系统耦合,外观类与系统耦合。
关键代码:在客户端和复杂系统之间再加一层,这一层将调用顺序、依赖关系等处理好。
应用实例: 1、去医院看病,可能要去挂号、门诊、划价、取药,让患者或患者家属觉得很复杂,如果有提供接待人员,只让接待人员来处理,就很方便。 2、JAVA 的三层开发模式。
优点: 1、减少系统相互依赖。 2、提高灵活性。 3、提高了安全性。
缺点:不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。
使用场景: 1、为复杂的模块或子系统提供外界访问的模块。 2、子系统相对独立。 3、预防低水平人员带来的风险。
注意事项:在层次化结构中,可以使用外观模式定义系统中每一层的入口。
桥接模式
桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。
这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。
我们通过下面的实例来演示桥接模式(Bridge Pattern)的用法。其中,可以使用相同的抽象类方法但是不同的桥接实现类,来画出不同颜色的圆。
介绍
意图:将抽象部分与实现部分分离,使它们都可以独立的变化。
主要解决:在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活。
何时使用:实现系统可能有多个角度分类,每一种角度都可能变化。
如何解决:把这种多角度分类分离出来,让它们独立变化,减少它们之间耦合。
关键代码:抽象类依赖实现类。
应用实例: 1、猪八戒从天蓬元帅转世投胎到猪,转世投胎的机制将尘世划分为两个等级,即:灵魂和肉体,前者相当于抽象化,后者相当于实现化。生灵通过功能的委派,调用肉体对象的功能,使得生灵可以动态地选择。 2、墙上的开关,可以看到的开关是抽象的,不用管里面具体怎么实现的。
优点: 1、抽象和实现的分离。 2、优秀的扩展能力。 3、实现细节对客户透明。
缺点:桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
使用场景: 1、如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。 2、对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。 3、一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
注意事项:对于两个独立变化的维度,使用桥接模式再适合不过了。
组合模式
组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。
我们通过下面的实例来演示组合模式的用法。实例演示了一个组织中员工的层次结构。
介绍
意图:将对象组合成树形结构以表示”部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
主要解决:它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
何时使用: 1、您想表示对象的部分-整体层次结构(树形结构)。 2、您希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
如何解决:树枝和叶子实现统一接口,树枝内部组合该接口。
关键代码:树枝内部组合该接口,并且含有内部属性 List,里面放 Component。
应用实例: 1、算术表达式包括操作数、操作符和另一个操作数,其中,另一个操作符也可以是操作数、操作符和另一个操作数。 2、在 JAVA AWT 和 SWING 中,对于 Button 和 Checkbox 是树叶,Container 是树枝。
优点: 1、高层模块调用简单。 2、节点自由增加。
缺点:在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。
使用场景:部分、整体场景,如树形菜单,文件、文件夹的管理。
注意事项:定义时为具体类。
享元模式
享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。
享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。我们将通过创建 5 个对象来画出 20 个分布于不同位置的圆来演示这种模式。由于只有 5 种可用的颜色,所以 color 属性被用来检查现有的 Circle 对象。
介绍
意图:运用共享技术有效地支持大量细粒度的对象。
主要解决:在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。
何时使用: 1、系统中有大量对象。 2、这些对象消耗大量内存。 3、这些对象的状态大部分可以外部化。 4、这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替。 5、系统不依赖于这些对象身份,这些对象是不可分辨的。
如何解决:用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象。
关键代码:用 HashMap 存储这些对象。
应用实例: 1、JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面。 2、数据库的数据池。
优点:大大减少对象的创建,降低系统的内存,使效率提高。
缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。
使用场景: 1、系统有大量相似对象。 2、需要缓冲池的场景。
注意事项: 1、注意划分外部状态和内部状态,否则可能会引起线程安全问题。 2、这些类必须有一个工厂对象加以控制。
行为型模式
策略模式
在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
介绍
意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
主要解决:在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。
何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。
如何解决:将这些算法封装成一个一个的类,任意地替换。
关键代码:实现同一个接口。
应用实例: 1、诸葛亮的锦囊妙计,每一个锦囊就是一个策略。 2、旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。 3、JAVA AWT 中的 LayoutManager。
优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
使用场景: 1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。 2、一个系统需要动态地在几种算法中选择一种。 3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
模板模式
在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。
介绍
意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
主要解决:一些方法通用,却在每一个子类都重新写了这一方法。
何时使用:有一些通用的方法。
如何解决:将这些通用算法抽象出来。
关键代码:在抽象类实现,其他步骤在子类实现。
应用实例: 1、在造房子的时候,地基、走线、水管都一样,只有在建筑的后期才有加壁橱加栅栏等差异。 2、西游记里面菩萨定好的 81 难,这就是一个顶层的逻辑骨架。 3、spring 中对 Hibernate 的支持,将一些已经定好的方法封装起来,比如开启事务、获取 Session、关闭 Session 等,程序员不重复写那些已经规范好的代码,直接丢一个实体就可以保存。
优点: 1、封装不变部分,扩展可变部分。 2、提取公共代码,便于维护。 3、行为由父类控制,子类实现。
缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
使用场景: 1、有多个子类共有的方法,且逻辑相同。 2、重要的、复杂的方法,可以考虑作为模板方法。
注意事项:为防止恶意操作,一般模板方法都加上 final 关键词。
观察者模式
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。
介绍
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
关键代码:在抽象类里有一个 ArrayList 存放观察者们。
应用实例: 1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。 2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。
优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景:
一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
一个对象必须通知其他对象,而并不知道这些对象是谁。
需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。注意事项: 1、JAVA 中已经有了对观察者模式的支持类。 2、避免循环引用。 3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
迭代器模式
迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式。这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。
迭代器模式属于行为型模式。
介绍
意图:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
主要解决:不同的方式来遍历整个整合对象。
何时使用:遍历一个聚合对象。
如何解决:把在元素之间游走的责任交给迭代器,而不是聚合对象。
关键代码:定义接口:hasNext, next。
应用实例:JAVA 中的 iterator。
优点: 1、它支持以不同的方式遍历一个聚合对象。 2、迭代器简化了聚合类。 3、在同一个聚合上可以有多个遍历。 4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
缺点:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
使用场景: 1、访问一个聚合对象的内容而无须暴露它的内部表示。 2、需要为聚合对象提供多种遍历方式。 3、为遍历不同的聚合结构提供一个统一的接口。
注意事项:迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。
责任链模式
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。
在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
介绍
意图:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
主要解决:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
何时使用:在处理消息的时候以过滤很多道。
如何解决:拦截的类都实现统一接口。
关键代码:Handler 里面聚合它自己,在 HanleRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 set 进去。
应用实例: 1、红楼梦中的”击鼓传花”。 2、JS 中的事件冒泡。 3、JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,Struts2 的拦截器,jsp servlet 的 Filter。
优点: 1、降低耦合度。它将请求的发送者和接收者解耦。 2、简化了对象。使得对象不需要知道链的结构。 3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。 4、增加新的请求处理类很方便。
缺点: 1、不能保证请求一定被接收。 2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。 3、可能不容易观察运行时的特征,有碍于除错。
使用场景: 1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。 2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。 3、可动态指定一组对象处理请求。
注意事项:在 JAVA WEB 中遇到很多应用。
命令模式
命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
介绍
意图:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
主要解决:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。
何时使用:在某些场合,比如要对行为进行”记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将”行为请求者”与”行为实现者”解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。
如何解决:通过调用者调用接受者执行命令,顺序:调用者→接受者→命令。
关键代码:定义三个角色:1、received 真正的命令执行对象 2、Command 3、invoker 使用命令对象的入口
应用实例:struts 1 中的 action 核心控制器 ActionServlet 只有一个,相当于 Invoker,而模型层的类会随着不同的应用有不同的模型类,相当于具体的 Command。
优点: 1、降低了系统耦合度。 2、新的命令可以很容易添加到系统中去。
缺点:使用命令模式可能会导致某些系统有过多的具体命令类。
使用场景:认为是命令的地方都可以使用命令模式,比如: 1、GUI 中每一个按钮都是一条命令。 2、模拟 CMD。
注意事项:系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作,也可以考虑使用命令模式,见命令模式的扩展。
备忘录模式
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。
介绍
意图:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
主要解决:所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。
何时使用:很多时候我们总是需要记录一个对象的内部状态,这样做的目的就是为了允许用户取消不确定或者错误的操作,能够恢复到他原先的状态,使得他有”后悔药”可吃。
如何解决:通过一个备忘录类专门存储对象状态。
关键代码:客户不与备忘录类耦合,与备忘录管理类耦合。
应用实例: 1、后悔药。 2、打游戏时的存档。 3、Windows 里的 ctri + z。 4、IE 中的后退。 4、数据库的事务管理。
优点: 1、给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。 2、实现了信息的封装,使得用户不需要关心状态的保存细节。
缺点:消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
使用场景: 1、需要保存/恢复数据的相关状态场景。 2、提供一个可回滚的操作。
注意事项: 1、为了符合迪米特原则,还要增加一个管理备忘录的类。 2、为了节约内存,可使用原型模式+备忘录模式。
状态模式
在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。
在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。
介绍
意图:允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。
何时使用:代码中包含大量与对象状态有关的条件语句。
如何解决:将各种具体的状态类抽象出来。
关键代码:通常命令模式的接口中只有一个方法。而状态模式的接口中有一个或者多个方法。而且,状态模式的实现类的方法,一般返回值,或者是改变实例变量的值。也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。状态模式和命令模式一样,也可以用于消除 if…else 等条件选择语句。
应用实例: 1、打篮球的时候运动员可以有正常状态、不正常状态和超常状态。 2、曾侯乙编钟中,’钟是抽象接口’,’钟A’等是具体状态,’曾侯乙编钟’是具体环境(Context)。
优点: 1、封装了转换规则。 2、枚举可能的状态,在枚举状态之前需要确定状态种类。 3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。 5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
缺点: 1、状态模式的使用必然会增加系统类和对象的个数。 2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。 3、状态模式对”开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
使用场景: 1、行为随状态改变而改变的场景。 2、条件、分支语句的代替者。
注意事项:在行为受状态约束的时候使用状态模式,而且状态不超过 5 个。
访问者模式
在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。
介绍
意图:主要将数据结构与数据操作分离。
主要解决:稳定的数据结构和易变的操作耦合问题。
何时使用:需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作”污染”这些对象的类,使用访问者模式将这些封装到类中。
如何解决:在被访问的类里面加一个对外提供接待访问者的接口。
关键代码:在数据基础类里面有一个方法接受访问者,将自身引用传入访问者。
应用实例:您在朋友家做客,您是访问者,朋友接受您的访问,您通过朋友的描述,然后对朋友的描述做出一个判断,这就是访问者模式。
优点: 1、符合单一职责原则。 2、优秀的扩展性。 3、灵活性。
缺点: 1、具体元素对访问者公布细节,违反了迪米特原则。 2、具体元素变更比较困难。 3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象。
使用场景: 1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。 2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作”污染”这些对象的类,也不希望在增加新操作时修改这些类。
注意事项:访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。
中介者模式
中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式。
介绍
意图:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
主要解决:对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,我们也需要跟踪与之相关联的对象,同时做出相应的处理。
何时使用:多个类相互耦合,形成了网状结构。
如何解决:将上述网状结构分离为星型结构。
关键代码:对象 Colleague 之间的通信封装到一个类中单独处理。
应用实例: 1、中国加入 WTO 之前是各个国家相互贸易,结构复杂,现在是各个国家通过 WTO 来互相贸易。 2、机场调度系统。 3、MVC 框架,其中C(控制器)就是 M(模型)和 V(视图)的中介者。
优点: 1、降低了类的复杂度,将一对多转化成了一对一。 2、各个类之间的解耦。 3、符合迪米特原则。
缺点:中介者会庞大,变得复杂难以维护。
使用场景: 1、系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象。 2、想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。
注意事项:不应当在职责混乱的时候使用。
解释器模式
解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
介绍
意图:给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。
主要解决:对于一些固定文法构建一个解释句子的解释器。
何时使用:如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。
如何解决:构件语法树,定义终结符与非终结符。
关键代码:构件环境类,包含解释器之外的一些全局信息,一般是 HashMap。
应用实例:编译器、运算表达式计算。
优点: 1、可扩展性比较好,灵活。 2、增加了新的解释表达式的方式。 3、易于实现简单文法。
缺点: 1、可利用场景比较少。 2、对于复杂的文法比较难维护。 3、解释器模式会引起类膨胀。 4、解释器模式采用递归调用方法。
使用场景: 1、可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。 2、一些重复出现的问题可以用一种简单的语言来进行表达。 3、一个简单语法需要解释的场景。
注意事项:可利用场景比较少,JAVA 中如果碰到可以用 expression4J 代替。
更多相关内容 -
24种设计模式介绍与6大设计原则
2017-11-08 19:25:2424种设计模式介绍与6大设计原则希望这本书的阅读者具备最基本的代码编写能力,您是一个初级的 coder,可以从中领会到怎么设计 一段优秀的代码;您是一个高级程序员,可以从中全面了解到设计模式以及 Java 的边角技术... -
android 24种设计模式介绍与6大设计原则
2016-08-11 16:59:09android 24种设计模式介绍与6大设计原则 -
24种设计模式(一)
2019-04-30 02:00:3224种设计模式 第1章 简单工厂 第2章 外观模式 第3章 适配器模式 第4章 单例模式 第5章 工厂方法模式 第6章 抽象工厂模式 第7章 生成器模式 第8章 原型模式 第9章 中介者模式 第10章 代理模式 第11章 观察...24种设计模式
Github 源码下载
第1章 简单工厂
1.简单工厂的定义
提供一个创建对象实例的功能,而无须关心其具体实现。被创建实例的类型可以是接口、抽象类,也可以是具体的类。
简单工厂的结构如图所示。
■ Api:定义客户所需要的功能接口。
■ Impl:具体实现Api的实现类,可能会有多个。
■ Factory:工厂,选择合适的实现类来创建Api接口对象。
■ Client:客户端,通过Factory来获取Api接口对象,然后面向Api接口编程。
2.简单工厂示例代码
public class Client { public static void main(String[] args) { SimpleFactory factory = new SimpleFactory(); GoodApi goodApi = factory.createGood(1); goodApi.operation(); } } interface GoodApi{ void operation(); } class GoodA implements GoodApi{ @Override public void operation() { System.out.println("GoodA"); } } class GoodB implements GoodApi{ @Override public void operation() { System.out.println("GoodB"); } } class SimpleFactory{ public GoodApi createGood(int type){ GoodApi goodApi = null; if(type==0){ goodApi = new GoodA(); }else if(type ==1 ){ goodApi = new GoodB(); } return goodApi; } }
3.静态工厂
使用简单工厂的时候,通常不用创建简单工厂类的类实例,没有创建实例的必要。因此可以把简单工厂类实现成一个工具类,直接使用静态方法就可以了。也就是说简单工厂的方法通常是静态的,所以也被称为静态工厂。如果要防止客户端无谓地创造简单工厂实例,还可以把简单工厂的构造方法私有化了。
4.简单工厂命名的建议
■ 类名称建议为“模块名称+Factory”。比如,用户模块的工厂就称为UserFactory。
■ 方法名称通常为“get+接口名称”或者是“create+接口名称”。比如,有一个接口名称为UserEbi,那么方法名称通常为getUserEbi或者是createUserEbi。
5. 可配置的简单工厂
使用配置文件,当有了新的实现类后,只要在配置文件里面配置上新的实现类即可。在简单工厂的方法里面可以使用反射,当然也可以使用IoC/DI(控制反转/依赖注入)来实现。
6 简单工厂的优缺点
简单工厂有以下优点。 ■ 帮助封装 简单工厂虽然很简单,但是非常友好地帮助我们实现了组件的封装,然后让组件外部能真正面向接口编程。 ■ 解耦 通过简单工厂,实现了客户端和具体实现类的解耦。 如同上面的例子,客户端根本就不知道具体是由谁来实现, 也不知道具体是如何实现的,客户端只是通过工厂获取它需要的接口对象。 简单工厂有以下缺点。 ■ 可能增加客户端的复杂度 如果通过客户端的参数来选择具体的实现类,那么就必须让客户端能理解各个参数所代表的具体功能和含义, 这样会增加客户端使用的难度,也部分暴露了内部实现,这种情况可以选用可配置的方式来实现。 ■ 不方便扩展子工厂 私有化简单工厂的构造方法,使用静态方法来创建接口,也就不能通过写简单工厂类的子类来改变创建接口的方法的行为了。 不过,通常情况下是不需要为简单工厂创建子类的。
7. 简单工厂的本质
简单工厂的本质是:选择实现。
简单工厂的目的在于为客户端来选择相应的实现,从而使得客户端和实现之间解耦。
第2章 外观模式(Facade)
1.外观模式的定义
外观模式就是通过引入这么一个外观类,在这个类里面定义客户端想要的简单的方法,然后在这些方法的实现里面,由外观类再去分别调用内部的多个模块来实现功能,从而让客户端变得简单。这样一来,客户端就只需要和外观类交互就可以了。
通俗讲就是一个大型系统对外有众多接口,不同的接口属于不同的服务。这对于另一个与其进行交互的系统来管理这么多接口是糟糕的事!故facade模式讲系统所有的接口都汇总到一个接口里(称之为门面),与其交互的系统只需要管理这一个接口就可以。
2. 外观模式示例代码
public class FacadeDemo { static class Client{ public static void main(String[] args) { Facade.opreation(); } } } interface ModelA{ void add(); } interface ModelB{ void delete(); } interface ModelC{ void update(); } class ModelAImpl implements ModelA{ @Override public void add() { System.out.println("modelA add()"); } } class ModelBImpl implements ModelB{ @Override public void delete() { System.out.println("modelA delete()"); } } class ModelCImpl implements ModelC{ @Override public void update() { System.out.println("modelC update()"); } } class Facade{ public static void opreation(){ ModelA modelA = new ModelAImpl(); modelA.add(); ModelB modelB = new ModelBImpl(); modelB.delete(); ModelC modelC = new ModelCImpl(); modelC.update(); } }
3.外观模式的目的
外观模式的目的不是给子系统添加新的功能接口,而是为了让外部减少与子系统内多个模块的交互,松散耦合,从而让外部能够更简单地使用子系统。
4. 外观模式的优缺点
外观模式有如下优点: ■ 松散耦合 外观模式松散了客户端与子系统的耦合关系,让子系统内部的模块能更容易扩展和维护。 ■ 简单易用 外观模式让子系统更加易用,客户端不再需要了解子系统内部的实现,也不需要跟众多子系统内部的模块进行交互, 只需要跟外观交互就可以了,相当于外观类为外部客户端使用子系统提供了一站式服务。 ■ 更好地划分访问的层次 通过合理使用Facade,可以帮助我们更好地划分访问的层次。 有些方法是对系统外的,有些方法是系统内部使用的。 把需要暴露给外部的功能集中到外观中,这样既方便客户端使用,也很好地隐藏了内部的细节。 外观模式有如下缺点: 过多的或者是不太合理的Facade也容易让人迷惑。到底是调用Facade好呢,还是直接调用模块好。
5.外观模式的本质
外观模式的本质是:封装交互,简化调用。
Facade封装了子系统外部和子系统内部多个模块的交互过程,从而简化了外部的调用。通过外观,子系统为外部提供一些高层的接口,以方便它们的使用。
6.何时选用外观模式
建议在如下情况时选用外观模式。
■ 如果你希望为一个复杂的子系统提供一个简单接口的时候,可以考虑使用外观模式。 使用外观对象来实现大部分客户需要的功能,从而简化客户的使用。 ■ 如果想要让客户程序和抽象类的实现部分松散耦合,可以考虑使用外观模式, 使用外观对象来将这个子系统与它的客户分离开来,从而提高子系统的独立性和可移植性。 ■ 如果构建多层结构的系统,可以考虑使用外观模式,使用外观对象作为每层的入口, 这样可以简化层间调用,也可以松散层次之间的依赖关系。
第3章 适配器模式(Adapter)
1.适配器模式的定义
将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
2. 适配器模式的结构和说明
■ Client:客户端,调用自己需要的领域接口Target。
■ Target:定义客户端需要的跟特定领域相关的接口。
■ Adaptee:已经存在的接口,通常能满足客户端的功能要求,但是接口与客户端要求的特定领域接口不一致,需要被适配。
■ Adapter:适配器,把Adaptee适配成为Client需要的Target。
3.示例代码
public class AdapterDemo { static class Client{ public static void main(String[] args) { Adaptee adaptee = new Adaptee(); Adapter adapter = new Adapter(adaptee); adapter.request(); } } } interface Target{ void request(); } class Adaptee{ public void existingRequest(){ System.out.println("已有接口"); } } class Adapter implements Target{ private Adaptee adaptee; public Adapter(Adaptee adaptee) { this.adaptee = adaptee; } @Override public void request() { System.out.println("适配现有接口"); adaptee.existingRequest(); } }
4. 适配器模式的优缺点
适配器模式有如下优点。 ■ 更好的复用性 如果功能是已经有了的,只是接口不兼容,那么通过适配器模式就可以让这些功能得到更好的复用。 ■ 更好的可扩展性 在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。 适配器模式有如下缺点。 ■ 过多地使用适配器,会让系统非常零乱,不容易整体进行把握。
5.适配器模式的本质
适配器模式的本质是:转换匹配,复用功能
适配器通过转换调用已有的实现,从而能把已有的实现匹配成需要的接口,使之能满足客户端的需要。也就是说转换匹配是手段,而复用已有的功能才是目的。
6.何时选用适配器模式
建议在以下情况中选用适配器模式。 ■ 如果你想要使用一个已经存在的类,但是它的接口不符合你的需求,这种情况可以使用适配器模式, 来把已有的实现转换成你需要的接口。 ■ 如果你想创建一个可以复用的类,这个类可能和一些不兼容的类一起工作,这种情况可以使用适配器模式, 到时候需要什么就适配什么。 ■ 如果你想使用一些已经存在的子类,但是不可能对每一个子类都进行适配,这种情况可以选用对象适配器, 直接适配这些子类的父类就可以了。
第4章 单例模式(Singleton)
1.单例模式的定义
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
2. 单例模式的结构和说明
Singleton:负责创建Singleton类自己的唯一实例,并提供一个getInstance的方法,让外部来访问这个类的唯一实例。
3. 单例模式示例代码
在Java中,单例模式的实现又分为两种,一种称为懒汉式,一种称为饿汉式,其实就是在具体创建对象实例的处理上,有不同的实现方式。下面分别来看看这两种实现方式的代码示例。
public class SingletonDemo { public static void main(String[] args) { System.out.println("懒汉式:"); SluggardSingleton sluggardSingleton = SluggardSingleton.getInstance(); System.out.println("饿汉式"); HungrySingleton hungrySingleton = HungrySingleton.getInstance(); } } /** * 懒汉式 */ class SluggardSingleton{ private static SluggardSingleton sluggardSingleton = null ; /** * 私有化构造器,不能通过new创建 */ private SluggardSingleton() { } public static synchronized SluggardSingleton getInstance(){ if(sluggardSingleton == null){ sluggardSingleton = new SluggardSingleton(); } return sluggardSingleton; } } /** * 饿汉式 */ class HungrySingleton{ private static HungrySingleton hungrySingleton = new HungrySingleton(); private HungrySingleton(){} public static synchronized HungrySingleton getInstance(){ return hungrySingleton; } }
所谓饿汉式,既然饿,那么在创建对象实例的时候就比较着急,饿了嘛,于是就在装载类的时候就创建对象实例。
所谓懒汉式,既然是懒,那么在创建对象实例的时候就不着急,会一直等到马上要使用对象实例的时候才会创建,懒人嘛,总是推托不开的时候才去真正执行工作,因此在装载对象的时候不创建对象实例。
4.单例模式的功能
单例模式是用来保证这个类在运行期间只会被创建一个类实例,另外,单例模式还提供了一个全局唯一访问这个类实例的访问点,就是getInstance方法。不管采用懒汉式还是饿汉式的实现方式,这个全局访问点是一样的。
对于单例模式而言,不管采用何种实现方式,它都是只关心类实例的创建问题,并不关心具体的业务功能。
单例模式的懒汉式实现方式体现了延迟加载的思想。
单例模式的懒汉式实现还体现了缓存的思想,缓存也是实际开发中常见的功能。
5. 单例模式的优缺点
1.时间和空间
比较上面两种写法:懒汉式是典型的时间换空间,也就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断的时间。当然,如果一直没有人使用的话,那就不会创建实例,则节约内存空间。
饿汉式是典型的空间换时间,当类装载的时候就会创建类实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断了,节省了运行时间。
2.线程安全
(1)从线程安全性上讲,不加同步的懒汉式是线程不安全的。
(2)饿汉式是线程安全的,因为虚拟机保证只会装载一次,在装载类的时候是不会发生并发的。(4)双重检查加锁
所谓双重检查加锁机制,指的是:并不是每次进入getInstance方法都需要同步,而是先不同步,进入方法过后,先检查实例是否存在,如果不存在才进入下面的同步块,这是第一重检查。进入同步块过后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。
双重检查加锁机制的实现会使用一个关键字volatile,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。这种实现方式既可以实现线程安全地创建实例,而又不会对性能造成太大的影响。它只是在第一次创建实例的时候同步,以后就不需要同步了,从而加快了运行速度。
由于volatile关键字可能会屏蔽掉虚拟机中一些必要的代码优化,所以运行效率并不是很高。因此一般建议,没有特别的需要,不要使用。也就是说,虽然可以使用“双重检查加锁”机制来实现线程安全的单例,但并不建议大量采用,可以根据情况来选用。
6. 在Java中一种更好的单例实现方式
■ 什么是类级内部类?
简单点说,类级内部类指的是,有static修饰的成员式内部类。如果没有static修饰的成员式内部类被称为对象级内部类。■ 类级内部类相当于其外部类的static成分,它的对象与外部类对象间不存在依赖关系,因此可直接创建。而对象级内部类的实例,是绑定在外部对象实例中的。
■ 类级内部类中,可以定义静态的方法。在静态方法中只能够引用外部类中的静态成员方法或者成员变量。
■ 类级内部类相当于其外部类的成员,只有在第一次被使用的时候才会被装载。
在多线程开发中,为了解决并发问题,主要是通过使用synchronized来加互斥锁进行同步控制。但是在某些情况中,JVM已经隐含地为您执行了同步,这些情况下就不用自己再来进行同步控制了。这些情况包括:
■ 由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据时
■ 访问final字段时
■ 在创建线程之前创建对象时
■ 线程可以看见它将要处理的对象时
6.1 解决方案的思路
要想很简单地实现线程安全,可以采用静态初始化器的方式,它可以由JVM来保证线程的安全性。
一种可行的方式就是采用类级内部类,在这个类级内部类里面去创建对象实例。这样一来,只要不使用到这个类级内部类,那就不会创建对象实例,从而同时实现延迟加载和线程安全。
示例代码:
public class SingletonDemo { public static void main(String[] args) { System.out.println("类级内部类方式"); SingletonDemo singletonDemo = SingletonDemo.getInstance(); } private static class SingletonHolder{ private static SingletonDemo singletonDemo = new SingletonDemo(); } public static SingletonDemo getInstance(){ return SingletonHolder.singletonDemo; } }
当getInstance方法第一次被调用的时候,它第一次读取SingletonHolder.instance,导致SingletonHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会在虚拟机装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。
这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。7.单例模式的本质
单例模式的本质:控制实例数目。
8.何时选用单例模式
建议在如下情况时,选用单例模式。
当需要控制一个类的实例只能有一个,而且客户只能从一个全局访问点访问它时,可以选用单例模式,这些功能恰好是单例模式要解决的问题。第5章 工厂方法模式 (Factory Method)
1.工厂方法(Factory Method)模式的定义
定义一个用于创建对象的接口,让子类决定实例化哪一个类,使一个类的实例化延迟到其子类。
2. 工厂方法模式的结构和说明
■ Product:定义工厂方法所创建的对象的接口,也就是实际需要使用的对象的接口。
■ ConcreteProduct:具体的Product接口的实现对象。
■ Creator:创建器,声明工厂方法,工厂方法通常会返回一个Product类型的实例对象,而且多是抽象方法。也可以在Creator里面提供工厂方法的默认实现,让工厂方法返回一个缺省的Product类型的实例对象。
■ ConcreteCreator:具体的创建器对象,覆盖实现Creator定义的工厂方法,返回具体的Product实例。
3. 工厂方法模式示例代码
public class FactoryMethodDemo { public static void main(String[] args) { Creater creater = new ConcreteCreaterA(); Product productA = creater.factoryMethod(); productA.printLog(); creater = new ConcreteCreaterB(); Product productB = creater.factoryMethod(); productB.printLog(); } } interface Product{ void printLog(); } class ConcreteProductA implements Product{ @Override public void printLog() { System.out.println("ConcreteProductA"); } } class ConcreteProductB implements Product{ @Override public void printLog() { System.out.println("ConcreteProductB"); } } abstract class Creater{ protected abstract Product factoryMethod(); public void someOpreations(){} } class ConcreteCreaterA extends Creater{ @Override protected Product factoryMethod() { return new ConcreteProductA(); } } class ConcreteCreaterB extends Creater{ @Override protected Product factoryMethod() { return new ConcreteProductB(); } }
4. 参数化工厂方法
可以改造工厂方法,去除抽象工厂类,参考简单工厂实现参数化工厂方法。
这是一种很常见的参数化工厂方法的实现方式,但是也还是有把参数化工厂方法实现成为抽象的,这点要注意,并不是说参数化工厂方法就不能实现成为抽象类了。只是一般情况下,参数化工厂方法,在父类都会提供默认的实现。
5. 工厂方法模式的优缺点
工厂方法模式的优点 ■ 可以在不知具体实现的情况下编程 工厂方法模式可以让你在实现功能的时候,如果需要某个产品对象,只需要使用产品的接口即可,而无需关心具体的实现。选择具体实现的任务延迟到子类去完成。 ■ 更容易扩展对象的新版本 工厂方法给子类提供了一个挂钩,使得扩展新的对象版本变得非常容易。比如上面示例的参数化工厂方法实现中,扩展一个新的导出xml文件格式的实现,已有的代码都不会改变,只要新加入一个子类来提供新的工厂方法实现,然后在客户端使用这个新的子类即可。 提示 另外这里提到的挂钩,就是我们经常说的钩子方法(hook),这个会在后面讲模板方法模式的时候详细点说明。 ■ 连接平行的类层次 工厂方法除了创造产品对象外,在连接平行的类层次上也大显身手。这个在前面已经详细讲述了。 工厂方法模式的缺点 ■ 具体产品对象和工厂方法的耦合性。 在工厂方法模式中,工厂方法是需要创建产品对象的,也就是需要选择具体的产品对象,并创建它们的实例,因此具体产品对象和工厂方法是耦合的。
6. 工厂方法模式的本质
工厂方法模式的本质:延迟到子类来选择实现。
第6章 抽象工厂模式(Abstract Factory)
1. 抽象工厂模式的定义
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
2. 抽象工厂模式的结构和说明
Abstract Factory:抽象工厂,定义创建一系列产品对象的操作接口。
■ Concrete Factory:具体的工厂,实现抽象工厂定义的方法,具体实现一系列产品对象的创建。
■ Abstract Product:定义一类产品对象的接口。
■ Concrete Product:具体的产品实现对象,通常在具体工厂里面,会选择具体的产品实现对象,来创建符合抽象工厂定义的方法返回的产品类型的对象。
■ Client:客户端,主要使用抽象工厂来获取一系列所需要的产品对象,然后面向这些产品对象的接口编程,以实现需要的功能。
3. 抽象工厂模式示例代码
public class AbstractFactoryMethodDemo { public static void main(String[] args) { AbstractFactoryMethod abstractFactoryMethod = new ConcreteFactoryMethodA1(); ProductA productA = abstractFactoryMethod.createA(); ProductB productB = abstractFactoryMethod.createB(); productA.printAlog(); productB.printBlog(); abstractFactoryMethod = new ConcreteFactoryMethodB1(); productA = abstractFactoryMethod.createA(); productB = abstractFactoryMethod.createB(); productA.printAlog(); productB.printBlog(); } } interface ProductA{ void printAlog(); } interface ProductB{ void printBlog(); } class ConcreteProductA1 implements ProductA{ @Override public void printAlog() { System.out.println("A1"); } } class ConcreteProductA2 implements ProductA{ @Override public void printAlog() { System.out.println("A2"); } } class ConcreteProductB1 implements ProductB{ @Override public void printBlog() { System.out.println("B1"); } } class ConcreteProductB2 implements ProductB{ @Override public void printBlog() { System.out.println("B2"); } } interface AbstractFactoryMethod{ ProductA createA(); ProductB createB(); } class ConcreteFactoryMethodA1 implements AbstractFactoryMethod{ @Override public ProductA createA() { return new ConcreteProductA1(); } @Override public ProductB createB() { return new ConcreteProductB1(); } } class ConcreteFactoryMethodB1 implements AbstractFactoryMethod{ @Override public ProductA createA() { return new ConcreteProductA2(); } @Override public ProductB createB() { return new ConcreteProductB2(); } }
4. 抽象工厂模式的优缺点
抽象工厂模式的优点 ■ 分离接口和实现 客户端使用抽象工厂来创建需要的对象,而客户端根本就不知道具体的实现是谁,客户端只是面向产品的接口编程而已。也就是说,客户端从具体的产品实现中解耦。 ■ 使得切换产品簇变得容易 因为一个具体的工厂实现代表的是一个产品簇,比如上面例子的Scheme1代表装机方案一:Intel的CPU+技嘉的主板,如果要切换成为Scheme2,那就变成了装机方案二:AMD的CPU+微星的主板。 客户端选用不同的工厂实现,就相当于是在切换不同的产品簇。 抽象工厂模式的缺点 ■ 不太容易扩展新的产品 前面也提到这个问题了,如果需要给整个产品簇添加一个新的产品,那么就需要修改抽象工厂,这样就会导致修改所有的工厂实现类。在前面提供了一个可以扩展工厂的方式来解决这个问题,但是又不够安全。如何选择,则要根据实际应用来权衡。 ■ 容易造成类层次复杂 在使用抽象工厂模式的时候,如果需要选择的层次过多,那么会造成整个类层次变得复杂。
5.抽象工厂模式的本质
抽象工厂模式的本质:选择产品簇的实现。
工厂方法是选择单个产品的实现,虽然一个类里面可以有多个工厂方法,但是这些方法之间一般是没有联系的,即使看起来像有联系。
但是抽象工厂着重的就是为一个产品簇选择实现,定义在抽象工厂里面的方法通常是有联系的,它们都是产品的某一部分或者是相互依赖的。如果抽象工厂里面只定义一个方法,直接创建产品,那么就退化成为工厂方法了。6.何时选用抽象工厂模式
建议在以下情况中选用抽象工厂模式。
■ 如果希望一个系统独立于它的产品的创建、组合和表示的时候。换句话说,希望一个系统只是知道产品的接口,而不关心实现的时候。
■ 如果一个系统要由多个产品系列中的一个来配置的时候。换句话说,就是可以动态地切换产品簇的时候。
■ 如果要强调一系列相关产品的接口,以便联合使用它们的时候。第7章 生成器模式(Builder)
1.生成器模式的定义
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
2. 生成器模式的结构和说明
■ Builder:生成器接口,定义创建一个Product对象所需的各个部件的操作。
■ ConcreteBuilder:具体的生成器实现,实现各个部件的创建,并负责组装Product对象的各个部件,同时还提供一个让用户获取组装完成后的产品对象的方法。
■ Director:指导者,也被称为导向者,主要用来使用Builder接口,以一个统一的过程来构建所需要的Product对象。
■ Product:产品,表示被生成器构建的复杂对象,包含多个部件。
3. 生成器模式示例代码
public class BuilderDemo { public static void main(String[] args) { Builder builder = new ConcreteBuilder(); Director director = new Director(builder); director.construct(); } } interface BuildProduct{ void someMethod(); } interface Builder{ void buildPart(); } class ConcreteBuilder implements Builder{ private String buildResult = null; public String getBuildResult() { return buildResult; } @Override public void buildPart() { System.out.println("构建产品细节。。。"); } } class Director{ private Builder builder; public Director(Builder builder) { this.builder = builder; } public void construct(){ builder.buildPart(); } }
4. 生成器模式的优点
■ 松散耦合
生成器模式可以用同一个构建算法构建出表现上完全不同的产品,实现产品构建和产品表现上的分离。生成器模式正是把产品构建的过程独立出来,使它和具体产品的表现松散耦合,从而使得构建算法可以复用,而具体产品表现也可以灵活地、方便地扩展和切换。
■ 可以很容易地改变产品的内部表示
在生成器模式中,由于Builder对象只是提供接口给Director使用,那么具体的部件创建和装配方式是被Builder接口隐藏了的,Director并不知道这些具体的实现细节。这样一来,要想改变产品的内部表示,只需要切换Builder的具体实现即可,不用管Director,因此变得很容易。
■ 更好的复用性
生成器模式很好地实现了构建算法和具体产品实现的分离。这样一来,使得构建产品的算法可以复用。同样的道理,具体产品的实现也可以复用,同一个产品的实现,可以配合不同的构建算法使用。
5.生成器模式的本质
生成器模式的本质:分离整体构建算法和部件构造。
构建一个复杂的对象,本来就有构建的过程,以及构建过程中具体的实现。生成器模式就是用来分离这两个部分,从而使得程序结构更松散、扩展更容易、复用性更好,同时也会使得代码更清晰,意图更明确。
6.何时选用生成器模式
建议在以下情况中选用生成器模式。
■ 如果创建对象的算法,应该独立于该对象的组成部分以及它们的装配方式时。
■ 如果同一个构建过程有着不同的表示时。
第8章 原型模式(Prototype)
1.原型模式的定义
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
2. 原型模式的结构和说明
■ Prototype:声明一个克隆自身的接口,用来约束想要克隆自己的类,要求它们都要实现这里定义的克隆方法。
■ ConcretePrototype:实现Prototype接口的类,这些类真正实现了克隆自身的功能。
■ Client:使用原型的客户端,首先要获取到原型实例对象,然后通过原型实例克隆自身来创建新的对象实例。
3. 原型模式示例代码
public class PrototypeDemo { public static void main(String[] args) { Prototype prototypeA = new PrototypeA(); prototypeA.setName("tom"); System.out.println(prototypeA.getName()); Client client = new Client(prototypeA); client.someOperation(); } } interface Prototype{ String getName(); void setName(String name); Prototype clone(); } class PrototypeA implements Prototype{ private String name ; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public Prototype clone() { Prototype prototypeA = new PrototypeA(); return prototypeA; } } class PrototypeB implements Prototype{ private String name ; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public Prototype clone() { Prototype prototypeB = new PrototypeA(); return prototypeB; } } class Client{ private Prototype prototype; public Client(Prototype prototype) { this.prototype = prototype; } public Prototype someOperation(){ Prototype newPrototype = prototype.clone(); newPrototype.setName("liom"); System.out.println(newPrototype.getName()); return newPrototype; } }
4.原型模式的功能
原型模式的功能实际上包含两个方面:
■ 一个是通过克隆来创建新的对象实例;
■ 另一个是为克隆出来的新的对象实例复制原型实例属性的值。
5. Java中的克隆方法
需要克隆功能的类,只需要实现java.lang.Cloneable接口,这个接口没有需要实现的方法,是一个标识接口。
6. 浅度克隆和深度克隆
■ 浅度克隆:只负责克隆按值传递的数据(比如基本数据类型、String类型)。
■ 深度克隆:除了浅度克隆要克隆的值外,还负责克隆引用类型的数据,基本上就是被克隆实例所有的属性数据都会被克隆出来。
7. 原型模式的优缺点
原型模式的优点:
■ 对客户端隐藏具体的实现类型
原型模式的客户端只知道原型接口的类型,并不知道具体的实现类型,从而减少了客户端对这些具体实现类型的依赖。
■ 在运行时动态改变具体的实现类型
原型模式可以在运行期间,由客户来注册符合原型接口的实现类型,也可以动态地改变具体的实现类型,看起来接口没有任何变化,但其实运行的已经是另外一个类实例了。因为克隆一个原型就类似于实例化一个类。
原型模式的缺点
原型模式最大的缺点就在于每个原型的子类都必须实现clone的操作,尤其在包含引用类型的对象时,clone方法会比较麻烦,必须要能够递归地让所有的相关对象都要正确地实现克隆。
8.原型模式的本质
原型模式的本质:克隆生成对象。
克隆是手段,目的是生成新的对象实例。正是因为原型的目的是为了生成新的对象实例,原型模式通常是被归类为创建型的模式。
原型模式也可以用来解决“只知接口而不知实现的问题”,使用原型模式,可以出现一种独特的“接口造接口”的景象,这在面向接口编程中很有用。同样的功能也可以考虑使用工厂来实现。
另外,原型模式的重心还是在创建新的对象实例,至于创建出来的对象,其属性的值是否一定要和原型对象属性的值完全一样,这个并没有强制规定,只不过在目前大多数实现中,克隆出来的对象和原型对象的属性值是一样的。
也就是说,可以通过克隆来创造值不一样的实例,但是对象类型必须一样。可以有部分甚至是全部的属性的值不一样,可以有选择性地克隆,就当是标准原型模式的一个变形使用吧。
9.何时选用原型模式
建议在以下情况时选用原型模式。
■ 如果一个系统想要独立于它想要使用的对象时,可以使用原型模式,让系统只面向接口编程,在系统需要新的对象的时候,可以通过克隆原型来得到。
■ 如果需要实例化的类是在运行时刻动态指定时,可以使用原型模式,通过克隆原型来得到需要的实例。
第9章 中介者模式(Mediator)
1.中介者模式的定义
用一个中介对象来封装一系列的对象交互。中介者使得各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
2. 中介者模式的结构和说明
■ Mediator:中介者接口。在里面定义各个同事之间交互需要的方法,可以是公共的通信方法,比如changed方法,大家都用,也可以是小范围的交互方法。
■ ConcreteMediator:具体中介者实现对象。它需要了解并维护各个同事对象,并负责具体的协调各同事对象的交互关系。
■ Colleague:同事类的定义,通常实现成为抽象类,主要负责约束同事对象的类型,并实现一些具体同事类之间的公共功能,比如,每个具体同事类都应该知道中介者对象,也就是具体同事类都会持有中介者对象,都可以定义到这个类里面。
■ ConcreteColleague:具体的同事类,实现自己的业务,在需要与其他同事通信的时候,就与持有的中介者通信,中介者会负责与其他的同事交互。
3. 中介者模式示例代码
public class MediatorDemo { public static void main(String[] args) { Mediator mediator = new ConcreteMediator(); ColleagueA colleague = new ColleagueA(mediator); colleague.someOperation(); ColleagueB colleagueB = new ColleagueB(mediator); colleagueB.someOperation(); } } interface Mediator{ void changed(Colleague colleague); } abstract class Colleague{ private Mediator mediator; public Colleague(Mediator mediator) { this.mediator = mediator; } public Mediator getMediator() { return mediator; } } class ColleagueA extends Colleague{ public ColleagueA(Mediator mediator) { super(mediator); } public void someOperation(){ getMediator().changed(this); } } class ColleagueB extends Colleague{ public ColleagueB(Mediator mediator) { super(mediator); } public void someOperation(){ getMediator().changed(this); } } class ConcreteMediator implements Mediator{ @Override public void changed(Colleague colleague) { if(colleague instanceof ColleagueA){ System.out.println("完成与A的交互操作"); }else if (colleague instanceof ColleagueB){ System.out.println("完成与B的交互操作"); } } }
4. 中介者模式的优缺点
中介者模式的优点。 ■ 松散耦合 中介者模式通过把多个同事对象之间的交互封装到中介者对象里面,从而使得同事对象之间松散耦合, 基本上可以做到互不依赖。 这样一来,同事对象就可以独立地变化和复用,而不再像以前那样“牵一发而动全身”了。 ■ 集中控制交互 多个同事对象的交互,被封装在中介者对象里面集中管理,使得这些交互行为发生变化的时候, 只需要修改中介者对象就可以了,当然如果是已经做好的系统,那就扩展中介者对象, 而各个同事类不需要做修改。 ■ 多对多变成一对多 没有使用中介者模式的时候,同事对象之间的关系通常是多对多的,引入中介者对象以后, 中介者对象和同事对象的关系通常变成了双向的一对多,这会让对象的关系更容易理解和实现。 中介者模式的缺点。 中介者模式的一个潜在缺点是,过度集中化。如果同事对象的交互非常多,而且比较复杂, 当这些复杂性全部集中到中介者的时候,会导致中介者对象变得十分复杂,而且难于管理和维护。
5.中介者模式的本质
中介者模式的本质:封装交互。
中介者模式的目的,就是用来封装多个对象的交互,这些交互的处理多在中介者对象里面实现。因此中介对象的复杂程度,就取决于它封装的交互的复杂程度。
6.何时选用中介者模式
建议在以下情况时选用中介者模式。
■ 如果一组对象之间的通信方式比较复杂,导致相互依赖、结构混乱,可以采用中介者模式,把这些对象相互的交互管理起来,各个对象都只需要和中介者交互,从而使得各个对象松散耦合,结构也更清晰易懂。
■ 如果一个对象引用很多的对象,并直接跟这些对象交互,导致难以复用该对象,可以采用中介者模式,把这个对象跟其他对象的交互封装到中介者对象里面,这个对象只需要和中介者对象交互就可以了。
第10章 代理模式(Proxy)
1.代理模式的定义
为其他对象提供一种代理以控制对这个对象的访问。
2. 代理模式的结构和说明
■ Proxy:代理对象,通常具有如下功能。
实现与具体的目标对象一样的接口,这样就可以使用代理来代替具体的目标对象。保存一个指向具体目标对象的引用,可以在需要的时候调用具体的目标对象。可以控制对具体目标对象的访问,并可以负责创建和删除它。
■ Subject:目标接口,定义代理和具体目标对象的接口,这样就可以在任何使用具体目标对象的地方使用代理对象。
■ RealSubject:具体的目标对象,真正实现目标接口要求的功能。
3. 代理模式示例代码
public class ProxyDemo { public static void main(String[] args) { RealSubject realSubject = new RealSubject(); Proxy proxy = new Proxy(realSubject); proxy.request(); } } interface Subject{ void request(); } class RealSubject implements Subject{ @Override public void request() { System.out.println("realSubject"); } } class Proxy implements Subject{ private RealSubject realSubject; public Proxy(RealSubject realSubject) { this.realSubject = realSubject; } @Override public void request() { System.out.println("开始请求前做一些校验操作。。。"); System.out.println("校验完成!"); realSubject.request(); System.out.println("请求结束做一些处理事宜。。。"); System.out.println("处理完成"); } }
4.代理的分类
事实上代理又被分成多种,大致有如下一些:
■ 虚代理:根据需要来创建开销很大的对象,该对象只有在需要的时候才会被真正创建。
■ 远程代理:用来在不同的地址空间上代表同一个对象,这个不同的地址空间可以是在本机,也可以在其他机器上。在Java里面最典型的就是RMI技术。
■ copy-on-write代理:在客户端操作的时候,只有对象确实改变了,才会真的拷贝(或克隆)一个目标对象,算是虚代理的一个分支。
■ 保护代理:控制对原始对象的访问,如果有需要,可以给不同的用户提供不同的访问权限,以控制他们对原始对象的访问。
■ Cache代理:为那些昂贵操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
■ 防火墙代理:保护对象不被恶意用户访问和操作。
■ 同步代理:使多个用户能够同时访问目标对象而没有冲突。
■ 智能指引:在访问对象时执行一些附加操作,比如,对指向实际对象的引用计数、第一次引用一个持久对象时,将它装入内存等。
5. Java中的代理
Java对代理模式提供了内建的支持,在java.lang.reflect包下面,提供了一个Proxy的类和一个InvocationHandler的接口。
6.代理模式的本质
代理模式的本质:控制对象访问。
代理模式通过代理目标对象,把代理对象插入到客户和目标对象之间,从而为客户和目标对象引入一定的间接性。正是这个间接性,给了代理对象很多的活动空间。代理对象可以在调用具体的目标对象前后,附加很多操作,从而实现新的功能或是扩展目标对象的功能。更狠的是,代理对象还可以不去创建和调用目标对象,也就是说,目标对象被完全代理掉了,或是被替换掉了。
从实现上看,代理模式主要是使用对象的组合和委托,尤其是在静态代理的实现里面,会看得更清楚。但是也可以采用对象继承的方式来实现代理,这种实现方式在某些情况下,比使用对象组合还要来得简单。
7.何时选用代理模式
建议在如下情况中选用代理模式。
■ 需要为一个对象在不同的地址空间提供局部代表的时候,可以使用远程代理。
■ 需要按照需要创建开销很大的对象的时候,可以使用虚代理。
■ 需要控制对原始对象的访问的时候,可以使用保护代理。
■ 需要在访问对象执行一些附加操作的时候,可以使用智能指引代理。
第11章 观察者模式(Observer)
1.观察者模式的定义
定义对象间的一种一对多的依赖关系。当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
2. 观察者模式的结构和说明
■ Subject:目标对象,通常具有如下功能。
◆ 一个目标可以被多个观察者观察。 ◆ 目标提供对观察者注册和退订的维护。 ◆ 当目标的状态发生变化时,目标负责通知所有注册的、有效的观察者。
■ Observer:定义观察者的接口,提供目标通知时对应的更新方法,这个更新方法进行相应的业务处理,可以在这个方法里面回调目标对象,以获取目标对象的数据。
■ ConcreteSubject:具体的目标实现对象,用来维护目标状态,当目标对象的状态发生改变时,通知所有注册的、有效的观察者,让观察者执行相应的处理。
■ ConcreteObserver:观察者的具体实现对象,用来接收目标的通知,并进行相应的后续处理,比如更新自身的状态以保持和目标的相应状态一致。
3. 使用观察者模式实现示例
import java.util.ArrayList; import java.util.List; public class ObserverDemo { public static void main(String[] args) { ConcreateObserver concreateObserver = new ConcreateObserver(); concreateObserver.setObserverState("202"); System.out.println(concreateObserver.getObserverState()); ConcreateSubject subjectA = new ConcreateSubject(); subjectA.attach(concreateObserver); subjectA.setSubjectState("404"); System.out.println(concreateObserver.getObserverState()); subjectA.detach(concreateObserver); subjectA.setSubjectState("500"); System.out.println(concreateObserver.getObserverState()); } } interface Observer{ void update(SubjectA subject); } class SubjectA{ private List<Observer> observerList = new ArrayList<Observer>(); //注册 public void attach(Observer observer){ observerList.add(observer); } //注销 public void detach(Observer observer){ observerList.remove(observer); } public void notifyObservers(){ for(Observer observer : observerList){ observer.update(this); } } } class ConcreateSubject extends SubjectA{ private String subjectState; public String getSubjectState() { return subjectState; } public void setSubjectState(String subjectState) { this.subjectState = subjectState; this.notifyObservers(); } } class ConcreateObserver implements Observer{ private String ObserverState; @Override public void update(SubjectA subject) { ObserverState = ((ConcreateSubject)subject).getSubjectState(); } public String getObserverState() { return ObserverState; } public void setObserverState(String observerState) { ObserverState = observerState; } }
4. 推模型和拉模型
在观察者模式的实现中,又分为推模型和拉模型两种方式。
■ 推模型
目标对象主动向观察者推送目标的详细信息,不管观察者是否需要,推送的信息通常是目标对象的全部或部分数据,相当于是在广播通信。
■ 拉模型
目标对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到目标对象中获取,相当于是观察者从目标对象中拉数据。一般这种模型的实现中,会把目标对象自身通过update方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。
5. 观察者模式的优缺点
观察者模式具有以下优点。
■ 观察者模式实现了观察者和目标之间的抽象耦合
原本目标对象在状态发生改变的时候,需要直接调用所有的观察者对象,但是抽象出观察者接口以后,目标和观察者就只是在抽象层面上耦合了,也就是说目标只是知道观察者接口,并不知道具体的观察者的类,从而实现目标类和具体的观察者类之间解耦。
■ 观察者模式实现了动态联动
所谓联动,就是做一个操作会引起其他相关的操作。由于观察者模式对观察者注册实行管理,那就可以在运行期间,通过动态地控制注册的观察者,来控制某个动作的联动范围,从而实现动态联动。
■ 观察者模式支持广播通信
由于目标发送通知给观察者是面向所有注册的观察者,所以每次目标通知的信息就要对所有注册的观察者进行广播。当然,也可以通过在目标上添加新的功能来限制广播的范围。
在广播通信的时候要注意一个问题,就是相互广播造成死循环的问题。比如A和B两个对象互为观察者和目标对象,A对象发生状态变化,然后A来广播信息,B对象接收到通知后,在处理过程中,使得B对象的状态也发生了改变,然后B来广播信息,然后A对象接到通知后,又触发广播信息……,如此A引起B变化,B又引起A变化,从而一直相互广播信息,就造成死循环。
观察者模式的缺点是:
■ 可能会引起无谓的操作
由于观察者模式每次都是广播通信,不管观察者需不需要,每个观察者都会被调用update方法,如果观察者不需要执行相应处理,那么这次操作就浪费了。其实浪费了还好,最怕引起误更新,那就麻烦了,比如,本应该在执行这次状态更新前把某个观察者删除掉,这样通知的时候就没有这个观察者了,但是现在忘掉了,那么就会引起误操作。
6. 观察者模式的本质
观察者模式的本质:触发联动。
当修改目标对象的状态的时候,就会触发相应的通知,然后会循环调用所有注册的观察者对象的相应方法,其实就相当于联动调用这些观察者的方法。
而且这个联动还是动态的,可以通过注册和取消注册来控制观察者,因而可以在程序运行期间,通过动态地控制观察者,来变相地实现添加和删除某些功能处理,这些功能就是观察者在update的时候执行的功能。
同时目标对象和观察者对象的解耦,又保证了无论观察者发生怎样的变化,目标对象总是能够正确地联动过来。
7.何时选用观察者模式
建议在以下情况中选用观察者模式。
■ 当一个抽象模型有两个方面,其中一个方面的操作依赖于另一个方面的状态变化,那么就可以选用观察者模式,将这两者封装成观察者和目标对象,当目标对象变化的时候,依赖于它的观察者对象也会发生相应的变化。这样就把抽象模型的这两个方面分离开了,使得它们可以独立地改变和复用。
■ 如果在更改一个对象的时候,需要同时连带改变其他的对象,而且不知道究竟应该有多少对象需要被连带改变,这种情况可以选用观察者模式,被更改的那一个对象很明显就相当于是目标对象,而需要连带修改的多个其他对象,就作为多个观察者对象了。
■ 当一个对象必须通知其他的对象,但是你又希望这个对象和其他被它通知的对象是松散耦合的。也就是说这个对象其实不想知道具体被通知的对象。这种情况可以选用观察者模式,这个对象就相当于是目标对象,而被它通知的对象就是观察者对象了。
第12章 命令模式(Command)
1.命令模式的定义
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
2. 命令模式的结构和说明
■ Command:定义命令的接口,声明执行的方法。
■ ConcreteCommand:命令接口实现对象,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
■ Receiver:接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
■ Invoker:要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
■ Client:创建具体的命令对象,并且设置命令对象的接收者。注意这个不是我们常规意义上的客户端,而是在组装命令对象和接收者,或许,把这个Client称为装配者会更好理解,因为真正使用命令的客户端是从Invoker来触发执行。
3. 命令模式示例代码
public class CommandDemo { public static void main(String[] args) { Receiver receiver = new Receiver(); Command command = new ConcreateCommand(receiver); Invoker invoker = new Invoker(); invoker.setCommand(command); invoker.runCommand(); } } interface Command{ void execute(); } class Receiver{ public void action(){ System.out.println("do something..."); } } class ConcreateCommand implements Command{ //命令自身状态 private String state; private Receiver receiver; public ConcreateCommand(Receiver receiver) { this.receiver = receiver; } @Override public void execute() { receiver.action(); } } class Invoker{ private Command command; public void setCommand(Command command) { this.command = command; } public void runCommand(){ command.execute(); } }
4. 命令模式的优点
■ 更松散的耦合
命令模式使得发起命令的对象——客户端,和具体实现命令的对象——接收者对象完全解耦,也就是说发起命令的对象完全不知道具体实现对象是谁,也不知道如何实现。■ 更动态的控制
命令模式把请求封装起来,可以动态地对它进行参数化、队列化和日志化等操作,从而使得系统更灵活。■ 很自然的复合命令
命令模式中的命令对象能够很容易地组合成复合命令,也就是前面讲的宏命令,从而使系统操作更简单,功能更强大。■ 更好的扩展性
由于发起命令的对象和具体的实现完全解耦,因此扩展新的命令就很容易,只需要实现新的命令对象,然后在装配的时候,把具体的实现对象设置到命令对象中,然后就可以使用这个命令对象,已有的实现完全不用变化。5.命令模式的本质
命令模式的本质:封装请求。
6.何时选用命令模式
建议在以下情况时选用命令模式。 ■ 如果需要抽象出需要执行的动作,并参数化这些对象,可以选用命令模式。 将这些需要执行的动作抽象成为命令,然后实现命令的参数化配置。 ■ 如果需要在不同的时刻指定、排列和执行请求,可以选用命令模式。 将这些请求封装成为命令对象,然后实现将请求队列化。 ■ 如果需要支持取消操作,可以选用命令模式,通过管理命令对象,能很容易地实现命令的恢复和重做功能。 ■ 如果需要支持当系统崩溃时,能将系统的操作功能重新执行一遍,可以选用命令模式。 将这些操作功能的请求封装成命令对象,然后实现日志命令,就可以在系统恢复以后,通过日志获取命令列表,从而重新执行一遍功能。 ■ 在需要事务的系统中,可以选用命令模式。命令模式提供了对事务进行建模的方法。命令模式有一个别名就是Transaction。
-
24种设计模式及其应用场景
2019-04-11 11:21:43Longronglin之设计模式: Christopher Alexander 说过:“每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复劳动”。 模式描述...Longronglin之设计模式:
Christopher Alexander 说过:“每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复劳动”。模式描述为: 在一定环境中解决某一问题的方案,包括三个基本元素--问题,解决方案和环境。阅读类图和对象图请先学习UML创建模式 结构模式 行为模式创建模式: 对类的实例化过程的抽象。一些系统在创建对象时,需要动态地决定怎样创建对象,创建哪些对象,以及如何组合和表示这些对象。创建模式描述了怎样构造和封装这些动态的决定。包含类的创建模式和对象的创建模式。结构模式: 描述如何将类或对象结合在一起形成更大的结构。分为类的结构模式和对象的结构模式。类的结构模式使用继承把类,接口等组合在一起,以形成更大的结构。类的结构模式是静态的。对象的结构模式描述怎样把各种不同类型的对象组合在一起,以实现新的功能的方法。对象的结构模式是动态的。行为模式: 对在不同的对象之间划分责任和算法的抽象化。不仅仅是关于类和对象的,并是关于他们之间的相互作用。类的行为模式使用继承关系在几个类之间分配行为。对象的行为模式则使用对象的聚合来分配行为。设计模式使用排行:频率所属类型模式名称模式简单定义5创建型Singleton单件保证一个类只有一个实例,并提供一个访问它的全局访问点。5结构型Composite组合模式将对象组合成树形结构以表示部分整体的关系,Composite使得用户对单个对象和组合对象的使用具有一致性。5结构型FAÇADE外观为子系统中的一组接口提供一致的界面,facade提供了一高层接口,这个接口使得子系统更容易使用。5结构型Proxy代理为其他对象提供一种代理以控制对这个对象的访问5行为型Iterator迭代器提供一个方法顺序访问一个聚合对象的各个元素,而又不需要暴露该对象的内部表示。5行为型Observer观察者定义对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知自动更新。5行为型Template Method模板方法定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,Template Method使得子类可以不改变一个算法的结构即可以重定义该算法得某些特定步骤。4创建型Abstract Factory抽象工厂提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们的具体类。4创建型Factory Method工厂方法定义一个用于创建对象的接口,让子类决定实例化哪一个类,Factory Method使一个类的实例化延迟到了子类。4结构型Adapter适配器将一类的接口转换成客户希望的另外一个接口,Adapter模式使得原本由于接口不兼容而不能一起工作那些类可以一起工作。4结构型Decorator装饰动态地给一个对象增加一些额外的职责,就增加的功能来说,Decorator模式相比生成子类更加灵活。4行为型Command命令将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队和记录请求日志,以及支持可撤销的操作。4行为型State状态允许对象在其内部状态改变时改变他的行为。对象看起来似乎改变了他的类。4行为型Strategy策略模式定义一系列的算法,把他们一个个封装起来,并使他们可以互相替换,本模式使得算法可以独立于使用它们的客户。3创建型Builder生成器将一个复杂对象的构建与他的表示相分离,使得同样的构建过程可以创建不同的表示。3结构型Bridge桥接将抽象部分与它的实现部分相分离,使他们可以独立的变化。3行为型China of Responsibility职责链使多个对象都有机会处理请求,从而避免请求的送发者和接收者之间的耦合关系2创建型Prototype原型用原型实例指定创建对象的种类,并且通过拷贝这些原型来创建新的对象。2结构型Flyweight享元享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能做到共享的关键是区分内蕴状态和外蕴状态。内蕴状态存储在享元内部,不会随环境的改变而有所不同。外蕴状态是随环境的改变而改变的。2行为型Mediator中介者用一个中介对象封装一些列的对象交互。2行为型Visitor访问者模式表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这个元素的新操作。1行为型Interpreter解释器给定一个语言,定义他的文法的一个表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。1行为型Memento备忘录在不破坏对象的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。一 : 单例模式(Singleton)单例模式 :Singleton的作用是保证在应用程序中,一个类Class只有一个实例存在。并提供全局访问。结构:账本类:1 单一实例 2 给多个对象共享 3 自己创建网页计数器
public class LazySingleton{
private static LazySingleton newInstance = null;private LazySingleton (){}public static synchronized LazySingleton getInstance (){
if (newInstance == null){
newInstance = new LazySingleton ();
}
return newInstance;
}}
singleton 限制了实例个数,有利于gc的回收。二: 策略模式(Strategy)策略模式:策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。策略模式把行为和环境分开。环境类负责维持和查询行为类,各种算法在具体的策略类中提供。由于算法和环境独立开来,算法的增减,修改都不会影响到环境和客户端。结构:使用QQ泡MM时使用外挂 客户端 :ME 抽象类: 外挂 具体:策略(图片,笑话,名人名言)图书销售算法(不同书本折扣的算法)三:原型模式(Prototype)原型模式:通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。原始模型模式允许动态的增加或减少产品类,产品类不需要非得有任何事先确定的等级结构,原始模型模式适用于任何的等级结构。缺点是每一个类都必须配备一个克隆方法结构:复印技术: 1 不是同一个对象 2 属同类短消息(转发) 1-n个MM因为Java中的提供clone()方法来实现对象的克隆,所以Prototype模式实现一下子变得很简单.四:门面模式(Façade)门面模式:外部与一个子系统的通信必须通过一个统一的门面对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用,减少复杂性。每一个子系统只有一个门面类,而且此门面类只有一个实例,也就是说它是一个单例模式。但整个系统可以有多个门面类。1 门面角色 2 子系统角色结构:Facade典型应用就是数据库JDBC的应用和Session的应用ME---àMM---à(father,mum,sister,brother)五:备忘录模式(Memento)Memento 模式:Memento对象是一个保存另外一个对象内部状态拷贝的对象,这样以后就可以将该对象恢复到原先保存的状态。模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。模式所涉及的角色有三个,备忘录角色、发起人角色和负责人角色。备忘录角色的作用:(1) 将发起人对象的内部状态存储起来,备忘录可以根据发起人对象的判断来决定存储多少发起人对象的内部状态。(2) 备忘录可以保护其内容不被发起人对象之外的任何对象所读取。发起人角色的作用:(1) 创建一个含有当前内部状态的备忘录对象。(2) 使用备忘录对象存储其内部状态。负责人角色的作用:(1) 负责保存备忘录对象。(2) 不检查备忘录对象的内容结构:备份系统时使用GHOST六 : 命令模式(Command)命令模式:命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。命令模式允许请求的一方和发送的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否执行,何时被执行以及是怎么被执行的。系统支持命令的撤消。结构:MM(客户端)--àME(请求者)--à命令角色--à(具体命令)-à代理处(接收者)--àMM上网 IE 输入 http地址 发送命令七: 解释器(Interpreter)解释器模式:给定一个语言后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。解释器模式将描述怎样在有了一个简单的文法后,使用模式设计解释这些语句。在解释器模式里面提到的语言是指任何解释器对象能够解释的任何组合。在解释器模式中需要定义一个代表文法的命令类的等级结构,也就是一系列的组合规则。每一个命令对象都有一个解释方法,代表对命令对象的解释。命令对象的等级结构中的对象的任何排列组合都是一个语言。结构:编译原理之编译器文言文注释:一段文言文,将它翻译成白话文八:调停者模式(Mediator)调停者模式:包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用。从而使他们可以松散偶合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。调停者模式将多对多的相互作用转化为一对多的相互作用。调停者模式将对象的行为和协作抽象化,把对象在小尺度的行为上与其他对象的相互作用分开处理。结构:法院和原告,被告的关系九:责任链模式(CHAIN OF RESPONSIBLEITY)责任链模式:执行者的不确定性 在责任链模式中,很多对象由每一个对象对其下家的引用而接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个对象最终处理这个请求,系统可以在不影响客户端的情况下动态的重新组织链和分配责任。处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。结构:典型的对象结构:喝酒时通过成语接龙决定谁喝酒(马到成功-功不可没-没完没了)十:工厂模式(Factory)工厂模式: 定义一个用于创建对象的接口,让接口子类通过工厂方法决定实例化哪一个类。结构:水果园—〉(葡萄园,苹果园)--〉(葡萄,苹果)(各自生产)十一:抽象工厂模式(Abstract Factory)抽象工厂模式: 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。结构:女娲造人---〉(阴,阳)--〉(人,兽)----〉(男人,女人,公兽,母兽)(人和兽属于不同的产品类)十二:建造模式(Builder)建造模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示.Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们.用户不知道内部的具体构建细节.Builder模式是非常类似抽象工厂模式,细微的区别大概只有在反复使用中才能体会到。将产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象。建造模式使得产品内部表象可以独立的变化,客户不必知道产品内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。结构:交互图:
汽车制造十三:合成模式(Composite)合成模式: 将对象以树形结构组织起来,以达成“部分-整体” 的层次结构,使得客户端对单个对象和组合对象的使用具有一致性. 合成模式就是一个处理对象的树结构的模式。合成模式把部分与整体的关系用树结构表示出来。合成模式使得客户端把一个个单独的成分对象和由他们复合而成的合成对象同等看待。结构:windows的目录树(文件系统)十四:装饰模式(DECORATOR)装饰模式:装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。动态给一个对象增加功能,这些功能可以再动态的撤消。增加由一些基本功能的排列组合而产生的非常大量的功能。使用Decorator的理由是:这些功能需要由用户动态决定加入的方式和时机.Decorator提供了"即插即用"的方法,在运行期间决定何时增加何种功能.结构:在visio中文件可以使用背景进行装饰变废为宝十五:设计模式之Adapter(适配器)适配器模式: 把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。适配类可以根据参数返还一个合适的实例给客户端
将两个不兼容的类纠合在一起使用,属于结构型模式,需要Adaptee(被适配者)和Adaptor(适配器)两个身份.为何使用?
我们经常碰到要将两个没有关系的类组合在一起使用,第一解决方案是:修改各自类的接口,但是如果我们没有源代码,或者,我们不愿意为了一个应用而修改各自的接口。 怎么办? 使用Adapter,在这两种接口之间创建一个混合接口(混血儿).如何使用?
实现Adapter方式,其实"think in Java"的"类再生"一节中已经提到,有两种方式:组合(composition)和继承(inheritance).结构:对象结构:充电器(手机和220V电压)jdbc-odbc桥十六:桥梁模式(Bridge)桥梁模式:将抽象化与实现化脱耦,使得二者可以独立的变化。也就是说将他们之间的强关联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立的变化。结构:jdbc驱动程序十七:代理模式(Proxy)代理模式:代理模式给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主题对象与真实主题对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系统的其他角色代为创建并传入。结构:运行时的代理结构:用代理服务器连接出网销售代理(厂商)律师代理(客户)foxmail枪手十八:享元模式(Flyweight)享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能做到共享的关键是区分内蕴状态和外蕴状态。内蕴状态存储在享元内部,不会随环境的改变而有所不同。外蕴状态是随环境的改变而改变的。外蕴状态不能影响内蕴状态,它们是相互独立的。将可以共享的状态和不可以共享的状态从常规类中区分开来,将不可以共享的状态从类里剔除出去。客户端不可以直接创建被共享的对象,而应当使用一个工厂对象负责创建被共享的对象。享元模式大幅度的降低内存中对象的数量。结构:共享方法:字体的26个字母和各自的斜体等十九:状态模式(State)状态模式:状态模式允许一个对象在其内部状态改变的时候改变行为。这个对象看上去象是改变了它的类一样。状态模式把所研究的对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式需要对每一个系统可能取得的状态创立一个状态类的子类。当系统的状态变化时,系统便改变所选的子类。结构:人心情不同时表现不同有不同的行为编钟登录login logout二十:观察者模式(Observer)观察者模式:观察者模式定义了一种一队多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。发布订阅。结构:公司邮件系统everyone@sina.com的应用。当公司员工向这个邮箱发邮件时会发给公司的每一个员工。如果设置了Outlook则会及时收到通知。接收到短消息二十一:模板方法模式(Template)模板方法模式:模板方法模式准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。结构:使用网页设计时使用的模板架构网页(骨架) 算法的各个逻辑系统二十二:访问者模式(Visitor)访问者模式:访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。访问者模式使得增加新的操作变的很容易,就是增加一个新的访问者类。访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。当使用访问者模式时,要将尽可能多的对象浏览逻辑放在访问者类中,而不是放到它的子类中。访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。结构:电脑销售系统: 访问者(自己)---〉电脑配置系统(主板,CPU,内存。。。。。。)二十三:迭代子模式(Iterator)迭代子模式:迭代子模式可以顺序访问一个聚集中的元素而不必暴露聚集的内部表象。多个对象聚在一起形成的总体称之为聚集,聚集对象是能够包容一组对象的容器对象。迭代子模式将迭代逻辑封装到一个独立的子对象中,从而与聚集本身隔开。迭代子模式简化了聚集的界面。每一个聚集对象都可以有一个或一个以上的迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。迭代算法可以独立于聚集角色变化。结构:查询数据库,返回结果集(map, list, set)二十四:MVC模式MVC 模式:它强制性的使应用程序的输入、处理和输出分开。使用MVC应用程序被分成三个核心部件:模型、视图、控制器。它们各自处理自己的任务。相互通信。MVC 还使用了的设计模式,如:用来指定视图缺省控制器的 Factory Method 和用来增加视图滚动的Decorator。但是 MVC 的主要关系还是由 Observer 、 Composite 和 Strategy 三个设计模式给出的。struts 图解:其中不同颜色代表MVC的不同部分:红色(控制器)、紫色(模型)和绿色(视图)struts应用 spring 应用设计模式的使用:模式关系图:个人图解:(^_^)没有看到下面的图解时想的门面模式可以使用一个单体实例对象实现抽象工厂可以创建单体实例 也可以使用工厂方法也可以使用原型创建对象实例模板方法可以使用工厂方法实现创建实例使用 策略模式定义算法使用策略模式可以使用享元实例 与装饰模式可以相互使用享元模式被状态,解释器,合成等模式。共享解释器模式通过访问模式实现其动作 通过享元实现基本元素的共享装饰模式使用策略可以实现不同的装饰效果迭代器模式通过访问者访问对象元素 通过备忘录模式实现纪录的记忆功能 访问合成的对象命令模式通过使用备忘录模式(参考) 执行命令建造模式可以使用合成模式创建合成产品责任链模式使用合成模式定义链调停者模式可以使观察者的观察受其影响实际图解:关模式(相互关系):
Abstract Factory 类通常用工厂方法( Factory Method )实现,但它们也可以用Prototype实现。一个具体的工厂通常是一个单件 Singleton 。 Abstract Factory 与Builder相似,因为它也可以创建复杂对象。主要的区别是Builder模式着重于一步步构造一个复杂对象。而 Abstract Factory 着重于多个系列的产品对象(简单的或是复杂的)。Builder在最后一步返回产品,而对于 Abstract Factory 来说,产品是立即返回的。Composite通常是用Builder生成的。Factory 方法通常在 Template Methods 中被调用。Prototype s 不需要创建Creator的子类。但是,它们通常要求一个针对Product类的Initialize操作。Creator使用Initialize来初始化对象。 Factory Method 不需要这样的操作。多态迭代器靠 Factory Method 来例化适当的迭代器子类。 Factory Method 模式常被模板方法调用。Prototype 和 Abstract Factory 模式在某种方面是相互竞争的。但是它们也可以一起使用。 Abstract Factory 可以存储一个被克隆的原型的集合,并且返回产品对象。大量使用Composite和Decorator模式的设计通常也可从Prototype模式处获益。很多模式可以使用 Singleton 模式实现。参见 Abstract Factory 、Builder,和Prototype。模式 Bridge 的结构与对象适配器类似,但是 Bridge 模式的出发点不同; Bridge 目的是将接口部分和实现部分分离,从而对它们可以较为容易也相对独立的加以改变。而 Adapter 则意味着改变一个 已有 对象的接口。Decorator 模式增强了其他对象的功能而同时又不改变它的接口。因此 Decorator 对应用程序的透明性比适配器要好。结果是 Decorator 支持递归组合,而纯粹使用适配器是不可能实现这一点的。模式 Proxy 在不改变它的接口的条件下,为另一个对象定义了一个代理。 Abstract Factory 模式可以用来创建和配置一个特定的Bridge模式。Adapter 模式用来帮助无关的类协同工作,它通常在系统设计完成后才会被使用。然而,Bridge模式则是在系统开始时就被使用,它使得抽象接口和实现部分可以独立进行改变。适配器 Adapter 为它所适配的对象提供了一个不同的接口。相反,代理提供了与它的实体相同的接口。然而,用于访问保护的代理可能会拒绝执行实体会执行的操作,因此,它的接口实际上可能只是实体接口的一个子集。Decorator 模式经常与Composite模式一起使用。当装饰和组合一起使用时,它们通常有一个公共的父类。因此装饰必须支持具有 Add 、 Remove 和 GetChild 操作。 Decorator 模式不同于 Adapter 模式,因为装饰仅改变对象的职责而不改变它的接口;而适配器将给对象一个全新的接口。Composite模式可以将装饰视为一个退化的、仅有一个组件的组合。然而,装饰仅给对象添加一些额外的职责—它的目的不在于对象聚集。用一个装饰你可以改变对象的外表;而Strategy模式使得你可以改变对象的内核。这是改变对象的两种途径。尽管 Decorator 的实现部分与 Proxy 相似,但 Decorator 的目的不一样。 Decorator 为对象添加一个或多个功能,而代理则控制对对象的访问。代理的实现 Decorator 的实现类似,但是在相似的程度上有所差别。 Protection Proxy 的实现可能与 Decorator 的实现差不多。另一方面, Remote Proxy 不包含对实体的直接引用,而只是一个间接引用,如“主机 I D ,主机上的局部地址。” Virtual Proxy 开始的时候使用一个间接引用,例如一个文件名,但最终将获取并使用一个直接引用。Abstract Factory 模式可以与 Facade 模式一起使用以提供一个接口,这一接口可用来以一种子系统独立的方式创建子系统对象。 Abstract Factory 也可以代替Facade模式隐藏那些与平台相关的类。 Mediator 模式与Facade模式的相似之处是,它抽象了一些已有的类的功能。然而, Mediator 的目的是对同事之间的任意通讯进行抽象,通常集中不属于任何单个对象的功能。 Mediator 的同事对象知道中介者并与它通信,而不是直接与其他同类对象通信。相对而言,Facade模式仅对子系统对象的接口进行抽象,从而使它们更容易使用;它并不定义新功能,子系统也不知道Facade的存在。通常来讲,仅需要一个Facade对象,因此Facade对象通常属于 Singleton 模式Chain of Responsibility 常与 Composite 一起使用。这种情况下,一个构件的父构件可作为它的后继。Composite 抽象语法树是一个复合模式的实例。Composite模式可被用来实现宏命令。Memento 可用来保持某个状态,命令用这一状态来取消它的效果。在被放入历史表列前必须被拷贝的命令起到一种原型的作用。 Memento 常与迭代器模式一起使用。迭代器可使用一个 Memento 来捕获一个迭代的状态。迭代器在其内部存储 Memento 。Flyweight 说明了如何在抽象语法树中共享终结符。Iterator 解释器可用一个迭代器遍历该结构。Visitor 可用来在一个类中维护抽象语法树中的各节点的行为。访问者可以用于对一个由Composite模式定义的对象结构进行操作。迭代器常被应用到象复合这样的递归结构上。Facade 与中介者的不同之处在于它是对一个对象子系统进行抽象,从而提供了一个更为方便的接口。它的协议是单向的,即Facade对象对这个子系统类提出请求,但反之则不行。相反, Mediator 提供了各Colleague对象不支持或不能支持的协作行为,而且协议是多向的。Colleague可使用Observer模式与 Mediator 通信。Command 命令可使用备忘录来为可撤消的操作维护状态。如前所述备忘录可用于迭代 。Mediator 通过封装复杂的更新语义。Singleton 使用 Singleton 模式来保证它是唯一的并且是可全局访问的。Flyweight 解释了何时以及怎样共享状态对象。状态对象通常是 Singleton 。Strategy对象经常是很好的轻量级对象。Strategy 模板方法使用继承来改变算法的一部分。Strategy使用委托来改变整个算法。Interpreter 访问者可以用于解释。创建型模式的讨论用一个系统创建的那些对象的类对系统进行参数化有两种常用方法。一种是生成创建对象的类的子类;这对应于使用 Factory Method 模式。这种方法的主要缺点是,仅为了改变产品类,就可能需要创建一个新的子类。这样的改变可能是级联的(Cascade)。例如,如果产品的创建者本身是由一个工厂方法创建的,那么你也必须重定义它的创建者。另一种对系统进行参数化的方法更多的依赖于对象复合:定义一个对象负责明确产品对象的类,并将它作为该系统的参数。这是 Abstract Factory 、Builder和Prototype模式的关键特征。所有这三个模式都涉及到创建一个新的负责创建产品对象的“工厂对象”。 Abstract Factory 由这个工厂对象产生多个类的对象。Builder由这个工厂对象使用一个相对复杂的协议,逐步创建一个复杂产品。 P rototype 由该工厂对象通过拷贝原型对象来创建产品对象。在这种情况下,因为原型负责返回产品对象,所以工厂对象和原型是同一个对象。结构型模式的讨论你可能已经注意到了结构型模式之间的相似性,尤其是它们的参与者和协作之间的相似性。这可能是因为结构型模式依赖于同一个很小的语言机制集合构造代码和对象:单继承和多重继承机制用于基于类的模式,而对象组合机制用于对象式模式。但是这些相似性掩盖了这些模式的不同意图。在本节中,我们将对比这些结构型模式,使你对它们各自的优点有所了解。Adapter 与 BridgeAdapter 模式和 Bridge 模式具有一些共同的特征。它们都给另一对象提供了一定程度上的间接性,因而有利于系统的灵活性。它们都涉及到从自身以外的一个接口向这个对象转发请求。这些模式的不同之处主要在于它们各自的用途。 Bridge 模式主要是为了解决两个已有接口之间不匹配的问题。它不考虑这些接口是怎样实现的,也不考虑它们各自可能会如何演化。这种方式不需要对两个独立设计的类中的任一个进行重新设计,就能够使它们协同工作。另一方面, Bridge 模式则对抽象接口与它的(可能是多个)实现部分进行桥接。虽然这一模式允许你修改实现它的类,它仍然为用户提供了一个稳定的接口。 Bridge 模式也会在系统演化时适应新的实现。由于这些不同点, Adapter 和 Bridge 模式通常被用于软件生命周期的不同阶段。当你发现两个不兼容的类必须同时工作时,就有必要使用 Adapter 模式,其目的一般是为了避免代码重复。此处耦合不可预见。相反, Bridge 的使用者必须事先知道:一个抽象将有多个实现部分,并且抽象和实现两者是独立演化的。 Adapter 模式在类已经设计好 后 实施;而 Bridge 模式在设计类之 前 实施。这并不意味着 Adapter 模式不如 Bridge 模式,只是因为它们针对了不同的问题。你可能认为facade是另外一组对象的适配器。但这种解释忽视了一个事实:即facade定义一个 新的 接口,而 Adapter 则复用一个原有的接口。记住,适配器使两个 已有的 接口协同工作,而不是定义一个全新的接口。Composite 、 Decorator 与 ProxyComposite 模式和 Decorator 模式具有类似的结构图,这说明它们都基于递归组合来组织可变数目的对象。这一共同点可能会使你认为, Decorator 对象是一个退化的 Composite ,但这一观点没有领会 Decorator 模式要点。相似点仅止于递归组合,同样,这是因为这两个模式的目的不同。 Decorator 旨在使你能够不需要生成子类即可给对象添加职责。这就避免了静态实现所有功能组合,从而导致子类急剧增加。 Composite 则有不同的目的,它旨在构造类,使多个相关的对象能够以统一的方式处理,而多重对象可以被当作一个对象来处理。它重点不在于修饰,而在于表示。尽管它们的目的截然不同,但却具有互补性。因此 Composite 和 Decorator 模式通常协同使用。在使用这两种模式进行设计时,我们无需定义新的类,仅需将一些对象插接在一起即可构建应用。这时系统中将会有一个抽象类,它有一些 Composite 子类和 Decorator 子类,还有一些实现系统的基本构建模块。此时, composites 和 decorator 将拥有共同的接口。从 Decorator 模式的角度看, Composite 是一个 ConcreteComponet 。 而从 Composite 模式的角度看, Decorator 则是一个 leaf 。当然,他们不一定要同时使用,正如我们所见,它们的目的有很大的差别。另一种与 Decorator 模式结构相似的模式是 Proxy 这两种模式都描述了怎样为对象提供一定程度上的间接引用, proxy 和 Decorator 对象的实现部分都保留了指向另一个对象的指针,它们向这个对象发送请求。然而同样,它们具有不同的设计目的。像 Decorator 模式一样, Proxy 模式构成一个对象并为用户提供一致的接口。但与 Decorator 模式不同的是, Proxy 模式不能动态地添加或分离性质,它也不是为递归组合而设计的。它的目的是,当直接访问一个实体不方便或不符合需要时,为这个实体提供一个替代者,例如,实体在远程设备上,访问受到限制或者实体是持久存储的。在 Proxy 模式中,实体定义了关键功能,而 Proxy 提供(或拒绝)对它的访问。在 Decorator 模式中,组件仅提供了部分功能,而一个或多个 Decorator 负责完成其他功能。 Decorator 模式适用于编译时不能(至少不方便)确定对象的全部功能的情况。这种开放性使递归组合成为 Decorator 模式中一个必不可少的部分。而在 Proxy 模式中则不是这样,因为 Proxy 模式强调一种关系( Proxy 与它的实体之间的关系),这种关系可以静态的表达。模式间的这些差异非常重要,因为它们针对了面向对象设计过程中一些特定的经常发生问题的解决方法。但这并不意味着这些模式不能结合使用。可以设想有一个 Proxy - Decorator ,它可以给 Proxy 添加功能,或是一个 Proxy - Proxy 用来修饰一个远程对象。尽管这种混合可能有用(我们手边还没有现成的例子),但它们可以分割成一些有用的模式。行为模式的讨论封装变化封装变化是很多行为模式的主题。当一个程序的某个方面的特征经常发生改变时,这些模式就定义一个封装这个方面的对象。这样当该程序的其他部分依赖于这个方面时,它们都可以与此对象协作。这些模式通常定义一个抽象类来描述这些封装变化的对象,并且通常该模式依据这个对象来命名。例如,• 一个Strategy对象封装一个算法• 一个Stat e 对象封装一个与状态相关的行为• 一个Mediator对象封装对象间的协议• 一个 Iterator 对象封装访问和遍历一个聚集对象中的各个构件的方法。这些模式描述了程序中很可能会改变的方面。大多数模式有两种对象:封装该方面特征的新对象,和使用这些新的对象的已有对象。如果不使用这些模式的话,通常这些新对象的功能就会变成这些已有对象的难以分割的一部分。例如,一个Strategy的代码可能会被嵌入到其 Context 类中,而一个Stat e 的代码可能会在该状态的 Context 类中直接实现。但不是所有的对象行为模式都象这样分割功能。例如, Chain of Responsibility )可以处理任意数目的对象(即一个链),而所有这些对象可能已经存在于系统中了。职责链说明了行为模式间的另一个不同点:并非所有的行为模式都定义类之间的静态通信关系。职责链提供在数目可变的对象间进行通信的机制。其他模式涉及到一些作为参数传递的对象。对象作为参数一些模式引入 总是 被用作参数的对象。例如Visitor。一个Visitor对象是一个多态的 Accept 操作的参数,这个操作作用于该Visitor对象访问的对象。虽然以前通常代替Visitor模式的方法是将Visito r 代码分布在一些对象结构的类中,但Visitor从来都不是它所访问的对象的一部分。其他模式定义一些可作为令牌到处传递的对象,这些对象将在稍后被调用。 Command 和 Memento 都属于这一类。在 Command 中,令牌代表一个请求;而在 Memento 中,它代表在一个对象在某个特定时刻的内部状态。在这两种情况下,令牌都可以有一个复杂的内部表示,但客户并不会意识到这一点。但这里还有一些区别:在 Command 模式中多态这个主题也贯穿于其他种类的模式。AbstractFactory, Builder( 3 . 2 ) 和 Prototype 都封装了关于对象是如何创建的信息。Decorator封装了可以被加入一个对象的职责。Bridge将一个抽象与它的实现分离,使它们可以各自独立的变化。很重要,因为执行 Command 对象是一个多态的操作。相反,Memento接口非常小,以至于备忘录只能作为一个值传递。因此它很可能根本不给它的客户提供任何多态操作。Mediator 和Observer是相互竞争的模式。它们之间的差别是, Observer通过引入Observer和 Subject 对象来分布通信,而 Mediatorr 对象则封装了其他对象间的通信。在Observer模式中,不存在封装一个约束的单个对象,而必须是由Observer和 Subject 对象相互协作来维护这个约束。通信模式由观察者和目标连接的方式决定:一个目标通常有多个观察者,并且有时一个目标的观察者也是另一个观察者的目标。 Mediator 模式的目的是集中而不是分布。它将维护一个约束的职责直接放在一个中介者中。我们发现生成可复用的Observer和 Subject 比生成可复用的 MMediator 容易一些。Observer模式有利于Observer和 Subject 间的分割和松耦合,同时这将产生粒度更细 , 从而更易于复用的类。另一方面,相对于 Subject , Mediator 中的通信流更容易理解。观察者和目标通常在它们被创建后很快即被连接起来,并且很难看出此后它们在程序中是如何连接的。如果你了解Observer r 模式,你将知道观察者和目标间连接的方式是很重要的,并且你也知道寻找哪些连接。然而, Observer模式引入的间接性仍然会使得一个系统难以理解。对发送者和接收者解耦当合作的对象直接互相引用时,它们变得互相依赖,这可能会对一个系统的分层和重用性产生负面影响。命令、观察者、中介者,和职责链等模式都涉及如何对发送者和接收者解耦,但它们又各有不同的权衡考虑。命令模式使用一个 Command 对象来定义一个发送者和一个接收者之间的绑定关系,从而支持解耦。观察者模式通过定义一个接口来通知目标中发生的改变,从而将发送者(目标)与接收者(观察者)解耦。Observer定义了一个比 Command 更松的发送者-接收者绑定,因为一个目标可能有多个观察者,并且其数目可以在运行时变化,因此当对象间有数据依赖时,最好用观察者模式来对它们进行解耦。中介者模式让对象通过一个Mediator对象间接的互相引用,从而对它们解耦。因此各 Colleague 对象仅能通过Mediator r 接口相互交谈。因为这个接口是固定的,为增加灵活性Mediator可能不得不实现它自己的分发策略。可以用一定方式对请求编码并打包参数,使得 Colleague 对象可以请求的操作数目不限。中介者模式可以减少一个系统中的子类生成,因为它将通信行为集中到一个类中而不是将其分布在各个子类中。然而,特别的分发策略通常会降低类型安全性。最后,职责链模式通过沿一个潜在接收者链传递请求而将发送者与接收者解耦,因为发送者和接收者之间的接口是固定的,职责链可能也需要一个定制的分发策略。因此它与Mediator一样存在类型安全的问题。如果职责链已经是系统结构的一部分,同时在链上的多个对象中总有一个可以处理请求,那么职责链将是一个很好的将发送者和接收者解耦的方法。此外,因为链可以被简单的改变和扩展,从而该模式提供了更大的灵活性。总结,除了少数例外情况,各个行为设计模式之间是相互补充和相互加强的关系。职责链可以使用Command模式将请求表示为对象。 Interpreter 可以使用State模式定义语法分析上下文。迭代器可以遍历一个聚合,而访问者可以对它的每一个元素进行一个操作。行为模式也与能其他模式很好地协同工作。例如,一个使用Composite模式的系统可以使用一个访问者对该复合的各成分进行一些操作。它可以使用职责链使得各成分可以通过它们的父类访问某些全局属性。它也可以使用 Decorator 对该复合的某些部分的这些属性进行改写。它可以使用 Observer 模式将一个对象结构与另一个对象结构联系起来,可以使用 State 模式使得一个构件在状态改变时可以改变自身的行为。复合本身可以使用 Builder 中的方法创建,并且它可以被系统中的其他部分当作一个 Prototype 。设计良好的面向对象式系统通常有多个模式镶嵌在其中,但其设计者却未必使用这些术语进行思考。然而,在 模式 级别而不是在类或对象级别上的进行系统组装可以使我们更方便地获取同等的协同性。参考文献:《Design Patterns》《Java与模式》《 设计模式 : 可复用面向对象软件的基础 》注:转载请注明出处和参考文献(本文的原著),请遵守相关法律,仅供学习研究。不得用于商业目的。
原文地址:http://blog.csdn.net/longronglin/article/details/1454315
-
24种设计模式介绍与6大设计原则.pdf
2010-03-21 14:51:4724种设计模式介绍与6大设计原则.pdf 24种设计模式介绍与6大设计原则.pdf 最好的24种设计模式介绍与6大设计原则.pdf -
24种设计模式大全-牛人详解
2016-09-25 22:13:11Longronglin之设计模式: Christopher Alexander 说过:“每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复劳动”。 模式描述为...Longronglin之设计模式:
Christopher Alexander 说过:“每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复劳动”。模式描述为: 在一定环境中解决某一问题的方案,包括三个基本元素--问题,解决方案和环境。阅读类图和对象图请先学习UML创建模式 结构模式 行为模式创建模式: 对类的实例化过程的抽象。一些系统在创建对象时,需要动态地决定怎样创建对象,创建哪些对象,以及如何组合和表示这些对象。创建模式描述了怎样构造和封装这些动态的决定。包含类的创建模式和对象的创建模式。结构模式: 描述如何将类或对象结合在一起形成更大的结构。分为类的结构模式和对象的结构模式。类的结构模式使用继承把类,接口等组合在一起,以形成更大的结构。类的结构模式是静态的。对象的结构模式描述怎样把各种不同类型的对象组合在一起,以实现新的功能的方法。对象的结构模式是动态的。行为模式: 对在不同的对象之间划分责任和算法的抽象化。不仅仅是关于类和对象的,并是关于他们之间的相互作用。类的行为模式使用继承关系在几个类之间分配行为。对象的行为模式则使用对象的聚合来分配行为。设计模式使用排行:频率所属类型模式名称模式简单定义5创建型Singleton单件保证一个类只有一个实例,并提供一个访问它的全局访问点。5结构型Composite组合模式将对象组合成树形结构以表示部分整体的关系,Composite使得用户对单个对象和组合对象的使用具有一致性。5结构型FAÇADE外观为子系统中的一组接口提供一致的界面,facade提供了一高层接口,这个接口使得子系统更容易使用。5结构型Proxy代理为其他对象提供一种代理以控制对这个对象的访问5行为型Iterator迭代器提供一个方法顺序访问一个聚合对象的各个元素,而又不需要暴露该对象的内部表示。5行为型Observer观察者定义对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知自动更新。5行为型Template Method模板方法定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,Template Method使得子类可以不改变一个算法的结构即可以重定义该算法得某些特定步骤。4创建型Abstract Factory抽象工厂提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们的具体类。4创建型Factory Method工厂方法定义一个用于创建对象的接口,让子类决定实例化哪一个类,Factory Method使一个类的实例化延迟到了子类。4结构型Adapter适配器将一类的接口转换成客户希望的另外一个接口,Adapter模式使得原本由于接口不兼容而不能一起工作那些类可以一起工作。4结构型Decorator装饰动态地给一个对象增加一些额外的职责,就增加的功能来说,Decorator模式相比生成子类更加灵活。4行为型Command命令将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队和记录请求日志,以及支持可撤销的操作。4行为型State状态允许对象在其内部状态改变时改变他的行为。对象看起来似乎改变了他的类。4行为型Strategy策略模式定义一系列的算法,把他们一个个封装起来,并使他们可以互相替换,本模式使得算法可以独立于使用它们的客户。3创建型Builder生成器将一个复杂对象的构建与他的表示相分离,使得同样的构建过程可以创建不同的表示。3结构型Bridge桥接将抽象部分与它的实现部分相分离,使他们可以独立的变化。3行为型China of Responsibility职责链使多个对象都有机会处理请求,从而避免请求的送发者和接收者之间的耦合关系2创建型Prototype原型用原型实例指定创建对象的种类,并且通过拷贝这些原型来创建新的对象。2结构型Flyweight享元享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能做到共享的关键是区分内蕴状态和外蕴状态。内蕴状态存储在享元内部,不会随环境的改变而有所不同。外蕴状态是随环境的改变而改变的。2行为型Mediator中介者用一个中介对象封装一些列的对象交互。2行为型Visitor访问者模式表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这个元素的新操作。1行为型Interpreter解释器给定一个语言,定义他的文法的一个表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。1行为型Memento备忘录在不破坏对象的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。一 : 单例模式(Singleton)单例模式 :Singleton的作用是保证在应用程序中,一个类Class只有一个实例存在。并提供全局访问。结构:账本类:1 单一实例 2 给多个对象共享 3 自己创建网页计数器
public class LazySingleton{
private static LazySingleton newInstance = null;private LazySingleton (){}public static synchronized LazySingleton getInstance (){
if (newInstance == null){
newInstance = new LazySingleton ();
}
return newInstance;
}}
singleton 限制了实例个数,有利于gc的回收。二: 策略模式(Strategy)策略模式:策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。策略模式把行为和环境分开。环境类负责维持和查询行为类,各种算法在具体的策略类中提供。由于算法和环境独立开来,算法的增减,修改都不会影响到环境和客户端。结构:使用QQ泡MM时使用外挂 客户端 :ME 抽象类: 外挂 具体:策略(图片,笑话,名人名言)图书销售算法(不同书本折扣的算法)三:原型模式(Prototype)原型模式:通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。原始模型模式允许动态的增加或减少产品类,产品类不需要非得有任何事先确定的等级结构,原始模型模式适用于任何的等级结构。缺点是每一个类都必须配备一个克隆方法结构:复印技术: 1 不是同一个对象 2 属同类短消息(转发) 1-n个MM因为Java中的提供clone()方法来实现对象的克隆,所以Prototype模式实现一下子变得很简单.四:门面模式(Façade)门面模式:外部与一个子系统的通信必须通过一个统一的门面对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用,减少复杂性。每一个子系统只有一个门面类,而且此门面类只有一个实例,也就是说它是一个单例模式。但整个系统可以有多个门面类。1 门面角色 2 子系统角色结构:Facade典型应用就是数据库JDBC的应用和Session的应用ME---àMM---à(father,mum,sister,brother)五:备忘录模式(Memento)Memento 模式:Memento对象是一个保存另外一个对象内部状态拷贝的对象,这样以后就可以将该对象恢复到原先保存的状态。模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。模式所涉及的角色有三个,备忘录角色、发起人角色和负责人角色。备忘录角色的作用:(1) 将发起人对象的内部状态存储起来,备忘录可以根据发起人对象的判断来决定存储多少发起人对象的内部状态。(2) 备忘录可以保护其内容不被发起人对象之外的任何对象所读取。发起人角色的作用:(1) 创建一个含有当前内部状态的备忘录对象。(2) 使用备忘录对象存储其内部状态。负责人角色的作用:(1) 负责保存备忘录对象。(2) 不检查备忘录对象的内容结构:备份系统时使用GHOST六 : 命令模式(Command)命令模式:命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。命令模式允许请求的一方和发送的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否执行,何时被执行以及是怎么被执行的。系统支持命令的撤消。结构:MM(客户端)--àME(请求者)--à命令角色--à(具体命令)-à代理处(接收者)--àMM上网 IE 输入 http地址 发送命令七: 解释器(Interpreter)解释器模式:给定一个语言后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。解释器模式将描述怎样在有了一个简单的文法后,使用模式设计解释这些语句。在解释器模式里面提到的语言是指任何解释器对象能够解释的任何组合。在解释器模式中需要定义一个代表文法的命令类的等级结构,也就是一系列的组合规则。每一个命令对象都有一个解释方法,代表对命令对象的解释。命令对象的等级结构中的对象的任何排列组合都是一个语言。结构:编译原理之编译器文言文注释:一段文言文,将它翻译成白话文八:调停者模式(Mediator)调停者模式:包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用。从而使他们可以松散偶合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。调停者模式将多对多的相互作用转化为一对多的相互作用。调停者模式将对象的行为和协作抽象化,把对象在小尺度的行为上与其他对象的相互作用分开处理。结构:法院和原告,被告的关系九:责任链模式(CHAIN OF RESPONSIBLEITY)责任链模式:执行者的不确定性 在责任链模式中,很多对象由每一个对象对其下家的引用而接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个对象最终处理这个请求,系统可以在不影响客户端的情况下动态的重新组织链和分配责任。处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。结构:典型的对象结构:喝酒时通过成语接龙决定谁喝酒(马到成功-功不可没-没完没了)十:工厂模式(Factory)工厂模式: 定义一个用于创建对象的接口,让接口子类通过工厂方法决定实例化哪一个类。结构:水果园—〉(葡萄园,苹果园)--〉(葡萄,苹果)(各自生产)十一:抽象工厂模式(Abstract Factory)抽象工厂模式: 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。结构:女娲造人---〉(阴,阳)--〉(人,兽)----〉(男人,女人,公兽,母兽)(人和兽属于不同的产品类)十二:建造模式(Builder)建造模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示.Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们.用户不知道内部的具体构建细节.Builder模式是非常类似抽象工厂模式,细微的区别大概只有在反复使用中才能体会到。将产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象。建造模式使得产品内部表象可以独立的变化,客户不必知道产品内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。结构:交互图:
汽车制造十三:合成模式(Composite)合成模式: 将对象以树形结构组织起来,以达成“部分-整体” 的层次结构,使得客户端对单个对象和组合对象的使用具有一致性. 合成模式就是一个处理对象的树结构的模式。合成模式把部分与整体的关系用树结构表示出来。合成模式使得客户端把一个个单独的成分对象和由他们复合而成的合成对象同等看待。结构:windows的目录树(文件系统)十四:装饰模式(DECORATOR)装饰模式:装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。动态给一个对象增加功能,这些功能可以再动态的撤消。增加由一些基本功能的排列组合而产生的非常大量的功能。使用Decorator的理由是:这些功能需要由用户动态决定加入的方式和时机.Decorator提供了"即插即用"的方法,在运行期间决定何时增加何种功能.结构:在visio中文件可以使用背景进行装饰变废为宝十五:设计模式之Adapter(适配器)适配器模式: 把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。适配类可以根据参数返还一个合适的实例给客户端
将两个不兼容的类纠合在一起使用,属于结构型模式,需要Adaptee(被适配者)和Adaptor(适配器)两个身份.为何使用?
我们经常碰到要将两个没有关系的类组合在一起使用,第一解决方案是:修改各自类的接口,但是如果我们没有源代码,或者,我们不愿意为了一个应用而修改各自的接口。 怎么办? 使用Adapter,在这两种接口之间创建一个混合接口(混血儿).如何使用?
实现Adapter方式,其实"think in Java"的"类再生"一节中已经提到,有两种方式:组合(composition)和继承(inheritance).结构:对象结构:充电器(手机和220V电压)jdbc-odbc桥十六:桥梁模式(Bridge)桥梁模式:将抽象化与实现化脱耦,使得二者可以独立的变化。也就是说将他们之间的强关联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立的变化。结构:jdbc驱动程序十七:代理模式(Proxy)代理模式:代理模式给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主题对象与真实主题对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系统的其他角色代为创建并传入。结构:运行时的代理结构:用代理服务器连接出网销售代理(厂商)律师代理(客户)foxmail枪手十八:享元模式(Flyweight)享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能做到共享的关键是区分内蕴状态和外蕴状态。内蕴状态存储在享元内部,不会随环境的改变而有所不同。外蕴状态是随环境的改变而改变的。外蕴状态不能影响内蕴状态,它们是相互独立的。将可以共享的状态和不可以共享的状态从常规类中区分开来,将不可以共享的状态从类里剔除出去。客户端不可以直接创建被共享的对象,而应当使用一个工厂对象负责创建被共享的对象。享元模式大幅度的降低内存中对象的数量。结构:共享方法:字体的26个字母和各自的斜体等十九:状态模式(State)状态模式:状态模式允许一个对象在其内部状态改变的时候改变行为。这个对象看上去象是改变了它的类一样。状态模式把所研究的对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式需要对每一个系统可能取得的状态创立一个状态类的子类。当系统的状态变化时,系统便改变所选的子类。结构:人心情不同时表现不同有不同的行为编钟登录login logout二十:观察者模式(Observer)观察者模式:观察者模式定义了一种一队多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。发布订阅。结构:公司邮件系统everyone@sina.com的应用。当公司员工向这个邮箱发邮件时会发给公司的每一个员工。如果设置了Outlook则会及时收到通知。接收到短消息二十一:模板方法模式(Template)模板方法模式:模板方法模式准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。结构:使用网页设计时使用的模板架构网页(骨架) 算法的各个逻辑系统二十二:访问者模式(Visitor)访问者模式:访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。访问者模式使得增加新的操作变的很容易,就是增加一个新的访问者类。访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。当使用访问者模式时,要将尽可能多的对象浏览逻辑放在访问者类中,而不是放到它的子类中。访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。结构:电脑销售系统: 访问者(自己)---〉电脑配置系统(主板,CPU,内存。。。。。。)二十三:迭代子模式(Iterator)迭代子模式:迭代子模式可以顺序访问一个聚集中的元素而不必暴露聚集的内部表象。多个对象聚在一起形成的总体称之为聚集,聚集对象是能够包容一组对象的容器对象。迭代子模式将迭代逻辑封装到一个独立的子对象中,从而与聚集本身隔开。迭代子模式简化了聚集的界面。每一个聚集对象都可以有一个或一个以上的迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。迭代算法可以独立于聚集角色变化。结构:查询数据库,返回结果集(map, list, set)二十四:MVC模式MVC 模式:它强制性的使应用程序的输入、处理和输出分开。使用MVC应用程序被分成三个核心部件:模型、视图、控制器。它们各自处理自己的任务。相互通信。MVC 还使用了的设计模式,如:用来指定视图缺省控制器的 Factory Method 和用来增加视图滚动的Decorator。但是 MVC 的主要关系还是由 Observer 、 Composite 和 Strategy 三个设计模式给出的。struts 图解:其中不同颜色代表MVC的不同部分:红色(控制器)、紫色(模型)和绿色(视图)struts应用 spring 应用设计模式的使用:
模式关系图:个人图解:(^_^)没有看到下面的图解时想的门面模式可以使用一个单体实例对象实现抽象工厂可以创建单体实例 也可以使用工厂方法也可以使用原型创建对象实例模板方法可以使用工厂方法实现创建实例使用 策略模式定义算法使用策略模式可以使用享元实例 与装饰模式可以相互使用享元模式被状态,解释器,合成等模式。共享解释器模式通过访问模式实现其动作 通过享元实现基本元素的共享装饰模式使用策略可以实现不同的装饰效果迭代器模式通过访问者访问对象元素 通过备忘录模式实现纪录的记忆功能 访问合成的对象命令模式通过使用备忘录模式(参考) 执行命令建造模式可以使用合成模式创建合成产品责任链模式使用合成模式定义链调停者模式可以使观察者的观察受其影响实际图解:关模式(相互关系):
Abstract Factory 类通常用工厂方法( Factory Method )实现,但它们也可以用Prototype实现。一个具体的工厂通常是一个单件 Singleton 。 Abstract Factory 与Builder相似,因为它也可以创建复杂对象。主要的区别是Builder模式着重于一步步构造一个复杂对象。而 Abstract Factory 着重于多个系列的产品对象(简单的或是复杂的)。Builder在最后一步返回产品,而对于 Abstract Factory 来说,产品是立即返回的。Composite通常是用Builder生成的。Factory 方法通常在 Template Methods 中被调用。Prototype s 不需要创建Creator的子类。但是,它们通常要求一个针对Product类的Initialize操作。Creator使用Initialize来初始化对象。 Factory Method 不需要这样的操作。多态迭代器靠 Factory Method 来例化适当的迭代器子类。 Factory Method 模式常被模板方法调用。Prototype 和 Abstract Factory 模式在某种方面是相互竞争的。但是它们也可以一起使用。 Abstract Factory 可以存储一个被克隆的原型的集合,并且返回产品对象。大量使用Composite和Decorator模式的设计通常也可从Prototype模式处获益。很多模式可以使用 Singleton 模式实现。参见 Abstract Factory 、Builder,和Prototype。模式 Bridge 的结构与对象适配器类似,但是 Bridge 模式的出发点不同; Bridge 目的是将接口部分和实现部分分离,从而对它们可以较为容易也相对独立的加以改变。而 Adapter 则意味着改变一个 已有 对象的接口。Decorator 模式增强了其他对象的功能而同时又不改变它的接口。因此 Decorator 对应用程序的透明性比适配器要好。结果是 Decorator 支持递归组合,而纯粹使用适配器是不可能实现这一点的。模式 Proxy 在不改变它的接口的条件下,为另一个对象定义了一个代理。 Abstract Factory 模式可以用来创建和配置一个特定的Bridge模式。Adapter 模式用来帮助无关的类协同工作,它通常在系统设计完成后才会被使用。然而,Bridge模式则是在系统开始时就被使用,它使得抽象接口和实现部分可以独立进行改变。适配器 Adapter 为它所适配的对象提供了一个不同的接口。相反,代理提供了与它的实体相同的接口。然而,用于访问保护的代理可能会拒绝执行实体会执行的操作,因此,它的接口实际上可能只是实体接口的一个子集。Decorator 模式经常与Composite模式一起使用。当装饰和组合一起使用时,它们通常有一个公共的父类。因此装饰必须支持具有 Add 、 Remove 和 GetChild 操作。 Decorator 模式不同于 Adapter 模式,因为装饰仅改变对象的职责而不改变它的接口;而适配器将给对象一个全新的接口。Composite模式可以将装饰视为一个退化的、仅有一个组件的组合。然而,装饰仅给对象添加一些额外的职责—它的目的不在于对象聚集。用一个装饰你可以改变对象的外表;而Strategy模式使得你可以改变对象的内核。这是改变对象的两种途径。尽管 Decorator 的实现部分与 Proxy 相似,但 Decorator 的目的不一样。 Decorator 为对象添加一个或多个功能,而代理则控制对对象的访问。代理的实现 Decorator 的实现类似,但是在相似的程度上有所差别。 Protection Proxy 的实现可能与 Decorator 的实现差不多。另一方面, Remote Proxy 不包含对实体的直接引用,而只是一个间接引用,如“主机 I D ,主机上的局部地址。” Virtual Proxy 开始的时候使用一个间接引用,例如一个文件名,但最终将获取并使用一个直接引用。Abstract Factory 模式可以与 Facade 模式一起使用以提供一个接口,这一接口可用来以一种子系统独立的方式创建子系统对象。 Abstract Factory 也可以代替Facade模式隐藏那些与平台相关的类。 Mediator 模式与Facade模式的相似之处是,它抽象了一些已有的类的功能。然而, Mediator 的目的是对同事之间的任意通讯进行抽象,通常集中不属于任何单个对象的功能。 Mediator 的同事对象知道中介者并与它通信,而不是直接与其他同类对象通信。相对而言,Facade模式仅对子系统对象的接口进行抽象,从而使它们更容易使用;它并不定义新功能,子系统也不知道Facade的存在。通常来讲,仅需要一个Facade对象,因此Facade对象通常属于 Singleton 模式Chain of Responsibility 常与 Composite 一起使用。这种情况下,一个构件的父构件可作为它的后继。Composite 抽象语法树是一个复合模式的实例。Composite模式可被用来实现宏命令。Memento 可用来保持某个状态,命令用这一状态来取消它的效果。在被放入历史表列前必须被拷贝的命令起到一种原型的作用。 Memento 常与迭代器模式一起使用。迭代器可使用一个 Memento 来捕获一个迭代的状态。迭代器在其内部存储 Memento 。Flyweight 说明了如何在抽象语法树中共享终结符。Iterator 解释器可用一个迭代器遍历该结构。Visitor 可用来在一个类中维护抽象语法树中的各节点的行为。访问者可以用于对一个由Composite模式定义的对象结构进行操作。迭代器常被应用到象复合这样的递归结构上。Facade 与中介者的不同之处在于它是对一个对象子系统进行抽象,从而提供了一个更为方便的接口。它的协议是单向的,即Facade对象对这个子系统类提出请求,但反之则不行。相反, Mediator 提供了各Colleague对象不支持或不能支持的协作行为,而且协议是多向的。Colleague可使用Observer模式与 Mediator 通信。Command 命令可使用备忘录来为可撤消的操作维护状态。如前所述备忘录可用于迭代 。Mediator 通过封装复杂的更新语义。Singleton 使用 Singleton 模式来保证它是唯一的并且是可全局访问的。Flyweight 解释了何时以及怎样共享状态对象。状态对象通常是 Singleton 。Strategy对象经常是很好的轻量级对象。Strategy 模板方法使用继承来改变算法的一部分。Strategy使用委托来改变整个算法。Interpreter 访问者可以用于解释。创建型模式的讨论用一个系统创建的那些对象的类对系统进行参数化有两种常用方法。一种是生成创建对象的类的子类;这对应于使用 Factory Method 模式。这种方法的主要缺点是,仅为了改变产品类,就可能需要创建一个新的子类。这样的改变可能是级联的(Cascade)。例如,如果产品的创建者本身是由一个工厂方法创建的,那么你也必须重定义它的创建者。另一种对系统进行参数化的方法更多的依赖于对象复合:定义一个对象负责明确产品对象的类,并将它作为该系统的参数。这是 Abstract Factory 、Builder和Prototype模式的关键特征。所有这三个模式都涉及到创建一个新的负责创建产品对象的“工厂对象”。 Abstract Factory 由这个工厂对象产生多个类的对象。Builder由这个工厂对象使用一个相对复杂的协议,逐步创建一个复杂产品。 P rototype 由该工厂对象通过拷贝原型对象来创建产品对象。在这种情况下,因为原型负责返回产品对象,所以工厂对象和原型是同一个对象。结构型模式的讨论你可能已经注意到了结构型模式之间的相似性,尤其是它们的参与者和协作之间的相似性。这可能是因为结构型模式依赖于同一个很小的语言机制集合构造代码和对象:单继承和多重继承机制用于基于类的模式,而对象组合机制用于对象式模式。但是这些相似性掩盖了这些模式的不同意图。在本节中,我们将对比这些结构型模式,使你对它们各自的优点有所了解。Adapter 与 BridgeAdapter 模式和 Bridge 模式具有一些共同的特征。它们都给另一对象提供了一定程度上的间接性,因而有利于系统的灵活性。它们都涉及到从自身以外的一个接口向这个对象转发请求。这些模式的不同之处主要在于它们各自的用途。 Bridge 模式主要是为了解决两个已有接口之间不匹配的问题。它不考虑这些接口是怎样实现的,也不考虑它们各自可能会如何演化。这种方式不需要对两个独立设计的类中的任一个进行重新设计,就能够使它们协同工作。另一方面, Bridge 模式则对抽象接口与它的(可能是多个)实现部分进行桥接。虽然这一模式允许你修改实现它的类,它仍然为用户提供了一个稳定的接口。 Bridge 模式也会在系统演化时适应新的实现。由于这些不同点, Adapter 和 Bridge 模式通常被用于软件生命周期的不同阶段。当你发现两个不兼容的类必须同时工作时,就有必要使用 Adapter 模式,其目的一般是为了避免代码重复。此处耦合不可预见。相反, Bridge 的使用者必须事先知道:一个抽象将有多个实现部分,并且抽象和实现两者是独立演化的。 Adapter 模式在类已经设计好 后 实施;而 Bridge 模式在设计类之 前 实施。这并不意味着 Adapter 模式不如 Bridge 模式,只是因为它们针对了不同的问题。你可能认为facade是另外一组对象的适配器。但这种解释忽视了一个事实:即facade定义一个 新的 接口,而 Adapter 则复用一个原有的接口。记住,适配器使两个 已有的 接口协同工作,而不是定义一个全新的接口。Composite 、 Decorator 与 ProxyComposite 模式和 Decorator 模式具有类似的结构图,这说明它们都基于递归组合来组织可变数目的对象。这一共同点可能会使你认为, Decorator 对象是一个退化的 Composite ,但这一观点没有领会 Decorator 模式要点。相似点仅止于递归组合,同样,这是因为这两个模式的目的不同。 Decorator 旨在使你能够不需要生成子类即可给对象添加职责。这就避免了静态实现所有功能组合,从而导致子类急剧增加。 Composite 则有不同的目的,它旨在构造类,使多个相关的对象能够以统一的方式处理,而多重对象可以被当作一个对象来处理。它重点不在于修饰,而在于表示。尽管它们的目的截然不同,但却具有互补性。因此 Composite 和 Decorator 模式通常协同使用。在使用这两种模式进行设计时,我们无需定义新的类,仅需将一些对象插接在一起即可构建应用。这时系统中将会有一个抽象类,它有一些 Composite 子类和 Decorator 子类,还有一些实现系统的基本构建模块。此时, composites 和 decorator 将拥有共同的接口。从 Decorator 模式的角度看, Composite 是一个 ConcreteComponet 。 而从 Composite 模式的角度看, Decorator 则是一个 leaf 。当然,他们不一定要同时使用,正如我们所见,它们的目的有很大的差别。另一种与 Decorator 模式结构相似的模式是 Proxy 这两种模式都描述了怎样为对象提供一定程度上的间接引用, proxy 和 Decorator 对象的实现部分都保留了指向另一个对象的指针,它们向这个对象发送请求。然而同样,它们具有不同的设计目的。像 Decorator 模式一样, Proxy 模式构成一个对象并为用户提供一致的接口。但与 Decorator 模式不同的是, Proxy 模式不能动态地添加或分离性质,它也不是为递归组合而设计的。它的目的是,当直接访问一个实体不方便或不符合需要时,为这个实体提供一个替代者,例如,实体在远程设备上,访问受到限制或者实体是持久存储的。在 Proxy 模式中,实体定义了关键功能,而 Proxy 提供(或拒绝)对它的访问。在 Decorator 模式中,组件仅提供了部分功能,而一个或多个 Decorator 负责完成其他功能。 Decorator 模式适用于编译时不能(至少不方便)确定对象的全部功能的情况。这种开放性使递归组合成为 Decorator 模式中一个必不可少的部分。而在 Proxy 模式中则不是这样,因为 Proxy 模式强调一种关系( Proxy 与它的实体之间的关系),这种关系可以静态的表达。模式间的这些差异非常重要,因为它们针对了面向对象设计过程中一些特定的经常发生问题的解决方法。但这并不意味着这些模式不能结合使用。可以设想有一个 Proxy - Decorator ,它可以给 Proxy 添加功能,或是一个 Proxy - Proxy 用来修饰一个远程对象。尽管这种混合可能有用(我们手边还没有现成的例子),但它们可以分割成一些有用的模式。行为模式的讨论封装变化封装变化是很多行为模式的主题。当一个程序的某个方面的特征经常发生改变时,这些模式就定义一个封装这个方面的对象。这样当该程序的其他部分依赖于这个方面时,它们都可以与此对象协作。这些模式通常定义一个抽象类来描述这些封装变化的对象,并且通常该模式依据这个对象来命名。例如,• 一个Strategy对象封装一个算法• 一个Stat e 对象封装一个与状态相关的行为• 一个Mediator对象封装对象间的协议• 一个 Iterator 对象封装访问和遍历一个聚集对象中的各个构件的方法。这些模式描述了程序中很可能会改变的方面。大多数模式有两种对象:封装该方面特征的新对象,和使用这些新的对象的已有对象。如果不使用这些模式的话,通常这些新对象的功能就会变成这些已有对象的难以分割的一部分。例如,一个Strategy的代码可能会被嵌入到其 Context 类中,而一个Stat e 的代码可能会在该状态的 Context 类中直接实现。但不是所有的对象行为模式都象这样分割功能。例如, Chain of Responsibility )可以处理任意数目的对象(即一个链),而所有这些对象可能已经存在于系统中了。职责链说明了行为模式间的另一个不同点:并非所有的行为模式都定义类之间的静态通信关系。职责链提供在数目可变的对象间进行通信的机制。其他模式涉及到一些作为参数传递的对象。对象作为参数一些模式引入 总是 被用作参数的对象。例如Visitor。一个Visitor对象是一个多态的 Accept 操作的参数,这个操作作用于该Visitor对象访问的对象。虽然以前通常代替Visitor模式的方法是将Visito r 代码分布在一些对象结构的类中,但Visitor从来都不是它所访问的对象的一部分。其他模式定义一些可作为令牌到处传递的对象,这些对象将在稍后被调用。 Command 和 Memento 都属于这一类。在 Command 中,令牌代表一个请求;而在 Memento 中,它代表在一个对象在某个特定时刻的内部状态。在这两种情况下,令牌都可以有一个复杂的内部表示,但客户并不会意识到这一点。但这里还有一些区别:在 Command 模式中多态这个主题也贯穿于其他种类的模式。AbstractFactory, Builder( 3 . 2 ) 和 Prototype 都封装了关于对象是如何创建的信息。Decorator封装了可以被加入一个对象的职责。Bridge将一个抽象与它的实现分离,使它们可以各自独立的变化。很重要,因为执行 Command 对象是一个多态的操作。相反,Memento接口非常小,以至于备忘录只能作为一个值传递。因此它很可能根本不给它的客户提供任何多态操作。Mediator 和Observer是相互竞争的模式。它们之间的差别是, Observer通过引入Observer和 Subject 对象来分布通信,而 Mediatorr 对象则封装了其他对象间的通信。在Observer模式中,不存在封装一个约束的单个对象,而必须是由Observer和 Subject 对象相互协作来维护这个约束。通信模式由观察者和目标连接的方式决定:一个目标通常有多个观察者,并且有时一个目标的观察者也是另一个观察者的目标。 Mediator 模式的目的是集中而不是分布。它将维护一个约束的职责直接放在一个中介者中。我们发现生成可复用的Observer和 Subject 比生成可复用的 MMediator 容易一些。Observer模式有利于Observer和 Subject 间的分割和松耦合,同时这将产生粒度更细 , 从而更易于复用的类。另一方面,相对于 Subject , Mediator 中的通信流更容易理解。观察者和目标通常在它们被创建后很快即被连接起来,并且很难看出此后它们在程序中是如何连接的。如果你了解Observer r 模式,你将知道观察者和目标间连接的方式是很重要的,并且你也知道寻找哪些连接。然而, Observer模式引入的间接性仍然会使得一个系统难以理解。对发送者和接收者解耦当合作的对象直接互相引用时,它们变得互相依赖,这可能会对一个系统的分层和重用性产生负面影响。命令、观察者、中介者,和职责链等模式都涉及如何对发送者和接收者解耦,但它们又各有不同的权衡考虑。命令模式使用一个 Command 对象来定义一个发送者和一个接收者之间的绑定关系,从而支持解耦。观察者模式通过定义一个接口来通知目标中发生的改变,从而将发送者(目标)与接收者(观察者)解耦。Observer定义了一个比 Command 更松的发送者-接收者绑定,因为一个目标可能有多个观察者,并且其数目可以在运行时变化,因此当对象间有数据依赖时,最好用观察者模式来对它们进行解耦。中介者模式让对象通过一个Mediator对象间接的互相引用,从而对它们解耦。因此各 Colleague 对象仅能通过Mediator r 接口相互交谈。因为这个接口是固定的,为增加灵活性Mediator可能不得不实现它自己的分发策略。可以用一定方式对请求编码并打包参数,使得 Colleague 对象可以请求的操作数目不限。中介者模式可以减少一个系统中的子类生成,因为它将通信行为集中到一个类中而不是将其分布在各个子类中。然而,特别的分发策略通常会降低类型安全性。最后,职责链模式通过沿一个潜在接收者链传递请求而将发送者与接收者解耦,因为发送者和接收者之间的接口是固定的,职责链可能也需要一个定制的分发策略。因此它与Mediator一样存在类型安全的问题。如果职责链已经是系统结构的一部分,同时在链上的多个对象中总有一个可以处理请求,那么职责链将是一个很好的将发送者和接收者解耦的方法。此外,因为链可以被简单的改变和扩展,从而该模式提供了更大的灵活性。总结,除了少数例外情况,各个行为设计模式之间是相互补充和相互加强的关系。职责链可以使用Command模式将请求表示为对象。 Interpreter 可以使用State模式定义语法分析上下文。迭代器可以遍历一个聚合,而访问者可以对它的每一个元素进行一个操作。行为模式也与能其他模式很好地协同工作。例如,一个使用Composite模式的系统可以使用一个访问者对该复合的各成分进行一些操作。它可以使用职责链使得各成分可以通过它们的父类访问某些全局属性。它也可以使用 Decorator 对该复合的某些部分的这些属性进行改写。它可以使用 Observer 模式将一个对象结构与另一个对象结构联系起来,可以使用 State 模式使得一个构件在状态改变时可以改变自身的行为。复合本身可以使用 Builder 中的方法创建,并且它可以被系统中的其他部分当作一个 Prototype 。设计良好的面向对象式系统通常有多个模式镶嵌在其中,但其设计者却未必使用这些术语进行思考。然而,在 模式 级别而不是在类或对象级别上的进行系统组装可以使我们更方便地获取同等的协同性。参考文献:《Design Patterns》《Java与模式》《 设计模式 : 可复用面向对象软件的基础 》注:转载请注明出处和参考文献(本文的原著),请遵守相关法律,仅供学习研究。不得用于商业目的。
原文地址:http://blog.csdn.net/longronglin/article/details/1454315
-
24种设计模式-工厂模式(3种)
2018-10-30 09:24:14工厂方法模式分为三种: 1、普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。首先看下关系图: 举例如下:(我们举一个发送邮件和短信的例子) 首先,创建二者的共同接口: ... -
24种设计模式与6大原则
2016-08-12 10:53:39总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元... -
白活了,阿里架构师10年心血汇成的《24 种设计模式》,这才是正解
2022-01-21 20:49:26设计模式 设计模式(Design Pattern)是...我曾立下个flag,要把23种设计模式全部记住,还曾买过《Head First设计模式》但是总是看完就忘,如此反复最后还是无用功。直到我遇见了阿里大佬耗费9年心血编辑而成的设计模式 -
设计模式大杂烩(24种设计模式的总结以及学习设计模式的几点建议)
2013-08-14 11:37:08迄今为止,LZ已经将24种设计模式介绍完了,其中包括GOF23种设计模式以及简单工厂模式,这些设计模式之间并不是完全独立的,而是互相之间,会有一些相同的影子,下面LZ分几个方式去对比和总结下这些设计模式。... -
Python七大原则,24种设计模式
2018-06-09 09:47:23七大原则,24种设计模式 七大设计原则: 1、单一职责原则【SINGLE RESPONSIBILITY PRINCIPLE】:一个类负责一项职责. 2、里氏替换原则【LISKOV SUBSTITUTION PRINCIPLE】:继承与派生的规则.(子类可替换父类) ... -
23种设计模式(概念、原则、场景、优点、缺点、应用)简述
2017-05-31 19:42:3623种设计模式:简单工厂模式,策略模式、装饰模式、代理模式、工厂方法模式、原型模式、模板方法模式、外观模式、建造者模式、观察者模式、抽象工厂模式、状态模式、适配器模式、备忘录模式、组合模式、迭代器模式、... -
ts中的设计模式
2022-03-07 18:56:17单列模式 整个应用只用实例化一次的类,常用于整个应用中的音频管理(比如游戏中的控制播放声音的操作) 无论调用多少次都只会初始化一次 class AudioManager { private static _instance = null; // 通过私有化... -
24种java设计模式总结和目录
2018-05-19 14:20:31设计模式是在特定环境下,为解决某一类通用软件设计问题提供的一套定制的解决方案,描述了对象和类之间的相互作用。 优点: 提供标准以供开发。方便交流,降低对复杂系统理解的难度。 更加简单方便的复用成功的设计... -
java设计模式(刘伟)
2018-12-14 22:53:38java设计模式,刘伟版。高清目录带书签。24种设计模式。 -
23种设计模式项目实例(包括源码和ppt)
2017-05-03 00:17:30项目源码(22个) -
24种设计模式复习总结
2013-08-29 02:41:1724种设计模式复习总结 -
前端开发中常用的几种设计模式
2021-08-17 15:05:54设计模式更多的是指导思想和方法论,而不是现成的代码,当然每种设计模式都有每种语言中的具体实现方式。学习设计模式更多的是理解各种模式的内在思想和解决的问题,毕竟这是前人无数经验总结成的最佳实践,而代码... -
Java开发中的23种设计模式详解及代码和图解
2016-08-24 15:24:01设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更... -
DP:***24种设计模式--转自刘伟
2014-02-10 09:31:272012年-2013年,Sunny在CSDN技术博客中陆续发表了100多篇与设计模式学习相关的文章,涵盖了七个面向对象设计原则和24个设计模式(23个GoF设计模式 + 简单工厂模式),为了方便大家学习,现将所有文章的链接进行了... -
24种设计模式与7大原则
2013-12-24 10:15:21创建型模式 抽象工厂模式(Abstract factory pattern): 提供一个接口, 用于创建相关或依赖对象的家族, 而不需要指定具体类. 生成器模式(Builder pattern): 使用生成器模式封装一个产品的构造过程, 并允许按步骤构造... -
什么是设计模式?程序员如何学好设计模式?
2021-11-30 00:15:58前几天,我给大家介绍了算法和数据结构的基础知识。后来又有小伙伴私信问我:“小灰,你能不能也讲一讲设计模式的相关知识?”没问题!对于程序员来说,设计模式也是必须要掌握的一项核心知识,我今天就... -
四人帮--23种设计模式
2018-10-11 20:05:10这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。 工厂模式(Factory Pattern) ... -
二十四种设计模式和六种设计原则
2017-09-11 18:42:19终于把这本买了一年的书浏览了一遍,《大话设计模式》,入门级好书,墙 裂 推荐 这回,对本书的精华之处做了总结 我是一条分割线 六大设计原则 ... -
26种设计模式,开发必备
2012-11-06 20:27:3026种设计模式 JAVA设计模式 JAVA开发设计模式 24种模式 工厂模式、代理模式、单例 多例 模式等等啦。开发面试必备。 -
设计模式PDF
2012-07-12 09:42:221.1 什么是设计模式 2 1.2 Smalltalk MVC中的设计模式 3 1.3 描述设计模式 4 1.4 设计模式的编目 5 1.5 组织编目 7 1.6 设计模式怎样解决设计问题 8 1.6.1 寻找合适的对象 8 1.6.2 决定对象的粒度 9 1.6.3 指定对象...