设计模式之设计模式及设计原则

来源:互联网 发布:怎么锻炼丁丁变粗 知乎 编辑:程序博客网 时间:2024/05/17 18:42

      模式是在某种情景下,针对某问题的某种解决方案。在我们使用过程中需要注意以下几点:

      (1)让设计模式自然而然地出现在你的设计中,而不是为了使用而使用。

      (2)设计模式并非僵化的教条,你可以依据自己的需要采用或调整。

      (3)总是使用满足需要的最简单的解决方案,不管它用不用模式。

一、策略模式

      设计原则一、找出应用中可能需要变化之处,把它们独立出来,不要和那些需要变化的代码混在一起

       在一个超类之中,我们会定义它的属性和行为,对于一些行为,不同的子类可能有不同的表现方式,在继承时我们可以通过覆盖来对行为进行改变,但这样势必会造成较多的工作量,因为每当子类行为作出改变,都要去检查覆盖对应的方法,因此我们可以把可能发生改变的行为提取出来。

      设计原则二、针对接口编程,而不是针对实现编程

       接上,当我们将可能变化的行为提出出接口后,如果让子类直接实现该接口,那么势必会造成代码的高重复性,因为我们不然子类实现该接口,而是创建一些其他的类来实现这些接口,这些类叫做行为类,用这些类来实现子类不同的行为。

       针对接口编程真正的意思是针对超类型编程,针对接口编程关键在多态,利用多态,程序针对超类型编程,执行时会根据超类中的方法执行到子类中的具体方法。针对超类型编程,更明确的说是变量的声明类型应该是超类型,通常是一个抽象类或者或者是接口,如此,只要是实现此超类型的类所产生的对象,都可以指定给这个变量,这也意味着,声明类时不用理会以后执行的真正的对象类型

      设计原则三、多用组合,少用继承

       接上,我们接超类的存在不同表现的行为提取出接口并实现一些行为类,在超类中,我们声明同样作为超类的接口变量,因此,在子类中,我们只需要将不同的行为类赋值给这个超类变量,就可以实现不同的行为类的表现方式,这种通过将两个类组合起来的方式建立的系统有很大的弹性,不仅可以将算法族封装成类,更可以在运行时动态的改变行为。

       以上三个设计原则的实现过程体现了一种设计模式,即策略模式,该模式的定义:策略模式定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于算法的客户。

二、观察者模式

        观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新,Java内置了观察者模式对象Observer接口和Observable对象。

       设计原则四、为了交互对象之间的松耦合设计而努力

        当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节,观察者模式提供了一种对象设计,让主题和观察者之间松耦合,对于观察者的一切,被观察者只需要知道观察者实现了一个接口,只需要注册或注销实现了该接口的观察者即可,被观察者和观察者其中一方改变都不会影响另一方,只要它们之间的接口仍被遵守,我们就尅自由的改变它们。松耦合的设计之所以能够让我们建立有弹性的oo系统,能够应付变化,是因为对象之间的相互依赖降到了最低。

三、装饰者模式

       设计原则五、类应该对扩展开放,对修改关闭。

        我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为,这样的设计具有弹性可以应对改变,可以接受新的功能来应对改变的需求。
装饰者模式动态地将责任附加到对象上,若要扩展功能,装饰者提供了比继承者更优弹性的替代方案。
        继承属于扩展形式之一,但不见得是达到弹性设计的最佳方案,在我们的设计中,应该允许行为可以被扩展,而无须修改现有的代码,组合和委托可用于运行时动态的加上新的行为,除了继承,装饰者模式也可以让我们扩展行为,装饰者模式意味着一群装饰者类,这些类用来包装具体组件,装饰者类反映出被装饰的组件类型,事实上,他们具有相同的类型,都经过接口或继承实现,装饰者可以在被装饰者的行为前面或后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的,你可以用无数个装饰者包装一个组件,装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型,装饰者会导致设计中出现许多小对象,如果多度使用,会让程序变得很复杂。

四、工厂模式

       所有工厂模式都用来封装对象的创建,工厂方法模式通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。
       工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,工厂方法让类实例化推迟到子类。工厂模式的组成元素有:创建者类(一般是抽象创建者类,它定义了一个抽象的工厂方法,让子类实现此方法创建对象)、产品类(工厂类创建的对象)。
       抽象工厂模式提供一个借口,用于创建相关或依赖对象的家族,而不需要明确指定类。
       工厂方法和抽象工厂的区别:
       (1)工厂方法:使用继承,把对象的创建委托给子类,用来创建对象,用于将实例化的具体类解耦,抽象工厂创建的对象集合可以用于工厂方法创建的具体类
       (2)抽象工厂:使用对象组合,对象的创建被实现在工厂接口所暴露出来的方法中,用于创建对象集合,创建每个对象会用工厂方法

      设计原则六、依赖倒置原则:要依赖抽象,不要依赖具体类。

       这个原则说明了:不能让高层组件依赖底层组件,而且,不管高层组件或底层组件,两者都应该依赖于抽象(所谓高层组件,是由其他底层组件定义其行为的类)
       避免在OO设计中违反依赖倒置原则的方法:
       (1)、变量不可以持有具体类的引用,如果使用new,就会持有具体类的引用,你可以改用工厂来避开这样的做法
       (2)、不要让类派生自具体类,如果派生自具体类,你就会依赖具体类,请派生自一个抽象(接口或者接口类)
       (3)、不要覆盖基类中已实现的方法,如果覆盖已实现的方法,那么你的基类就不是真正适合被继承的抽象类,基类中已实现的方法,应该由所有的子类共享

五、单例模式

       单例模式确保一个类只有一个实例,并提供一个全局访问点
       单例模式的实现:
       (1)、直接将getInstance方法设置同步:这样在多线程下可以保证只有一个实例,缺点是每次获取实例都会进入同步方法,性能较差
public static synchronized Singleton getInstance() {           if (single == null) {                 single = new Singleton();           }            return single;  }  
       (2)、急切实例化(饿汉式):直接静态的初始化
public class Singleton {          private static final Singleton INSTANCE = new Singleton();           private Singleton (){}        public static final Singleton getInstance() {           return LazyHolder.INSTANCE;        }    }    
       (3)、双重检查实例化(懒汉式):
public static Singleton getInstance() {          if (singleton == null) {                synchronized (Singleton.class) {                   if (singleton == null) {                      singleton = new Singleton();                  }                }            }            return singleton;       }  

六、命令模式

         命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象,命令模式也支持可撤销的操作。
         命令模式将发出请求的对象和执行请求的对象解耦,在被解耦的两者之间是通过命令对象就行沟通的,命令对象封装了接收者和一个或一组动作,调用者通过调用命令对象的execute()发出请求,这会使得接收者的动作被调用。调用者可以接受命令当做参数,甚至在运行时动态的运行,命令可以支持撤销,做法是实现一个undo()方法来回倒execute()被执行前的状态,宏命令是命令的一种简单的眼神,允许调用多个命令,宏方法也可以支持撤销,实际操作时,很常见使用"聪明"命令对象,也就是直接实现了请求,而不是将工作委托给接收者,命令也可以用来实现日志和事务系统。

七、适配器模式与外观模式

       适配器模式将一个类的接口,转换成客户期望的另一个接口,适配器让原本接口不兼容的类可以合作无间,它改变接口的目的是实现符合客户期望的接口。
       外观模式提供了一个统一的接口,用来访问子系统中的一群接口,外观定义了一个高层接口,让子系统更容易使用,它改变接口的目的则是为了简化接口,外观模式不只是简化了接口,也将客户从组件的子系统中解耦。
       外观模式和适配器模式的差异,不在于包装多少个类,而在于他们的意图,适配器模式的意图是将接口转换成不同的接口,外观模式的意图是简化接口。
       当需要使用一个现有的类而其接口并不符合你的需要时,就使用适配器,当需要简化并统一一个很大的接口或者一群复杂的接口时,使用外观,适配器改变接口以符合客户的期望,外观将客户从一个复杂的子系统中解耦,实现一个适配器需要一番功夫,也可能不费工夫,视目标接口的大小与复杂度而定,实现一个外观,需要将子系统组合进外观中,然后将工作委托给子系统执行,适配器模式有两种形式,对象适配器和类适配器,类适配器需要用到多重继承,你可以为一个子系统实现一个以上的外观,适配器将一个对象包装起来以改变其接口,装饰者将一个对象包装起来以增加新的行为和责任,而外观将一群对象包装起来以简化其接口。

       设计原则七、最少知识原则:只和你的密友谈话(一个对象应当对其他对象有尽可能少的了解)

        这就是说当你这在设计一个系统,不管是任何对象,你都要注意它所交互的类有哪些,并注意它和这些类是如何交互的,这个原则希望我们在设计中,不要让太多的类耦合在一起,免得修改系统的一部分,会影响到其他部分,如果许多类之间相互依赖,那么这个系统就会变成一个易碎的系统,它需要花许多成本维护,也许因为太复杂而不容易被其他人了解。针对这一原则,我们在对象的方法中,只应该调用属于以下范围的方法:
      (1)该对象本身   (2)被当做方法的参数而传递进来的对象   (3)此方法所创建或实例化的任何对象   (4)对象的任何组件
前三条告诉我们如果某个对象是调用其他方法的返回结果,不要调用该对象的方法。

八、模板方法模式

       模板方法定义了一个算法的步骤,并允许子类为一个或者多个步骤提供实现(父类定义步骤,由子类实现)。
       模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。这个模式是用来创建一个算法的模板(一个方法),这个方法将算法定义成一组步骤,其中的任何步骤都可以使抽象的,由子类负责实现,这可以确保算法的结构保持不变,同时由子类提供部分实现。相应的,我们可以在算法中加入一个钩子,让子类有能力对算法的不同点进行挂钩,要不要实现由子类决定。模板方法的抽象类可以定义具体方法、抽象方法和钩子,抽象方法由子类实现,钩子是一种方法,它在抽象类中不做事情,或者只做默认的事情,子类可以原则要不要去覆盖它,为了防止子类改变模板方法中的算法,可以将模板方法声明为final,工厂方法是模板方法的一种特殊版本。

      设计原则八、好莱坞原则:别调用我们,我们会调用你

       避免高层组件和底层组件之间环状调用。

九、迭代器和组合模式

       迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而不是暴露其内部的表示,这个模式很有意义,它提供了一种方法,可以顺序访问一个聚集对象中的元素,而又不用知道内部是如何表示的(创建一个接口,子类实现该接口并定义hasnext和next方法,在需要遍历的类中获取该迭代器进行迭代)。
       迭代器允许访问聚合的元素,而不需要暴露它的内部结构,迭代器将遍历聚合的工作封装进一个对象中,当使用迭代器的时候,我们依赖聚合提供遍历,迭代器提供了一个通用的接口,让我们遍历聚合的项,当我们编码使用聚合的项时,就可以使用多态机制。

      设计原则九、一个类应该只有一个引起变化的原因

       类的每个责任都有改变的潜在区域,超过一个责任,意味着超过一个改变的区域,这个原则告诉我们,尽量让每个类保持单一责任。
       组合模式允许你将对象组合成树形结构来表现"整体/部分"层次结构,组合能让客户以一致的方式处理个别对象以及对象组合,组合模式提供一个结构,可同时包容个别对象和组合对象,组合模式允许客户对个别对象以及组合对象一视同仁,组合结构内的任意对象称为组件,组件可以是组合(包含子类),也可以使叶节点(不包含子类)。在实现组合模式时,有许多设计上的折中,要根据需要平衡透明性和安全性。

十、状态模式

       状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
       这个模式将状态封装成独立的类,并将动作委托到代表当前状态的对象,我们知道行为会随着内部状态的改变而改变,对象看起来好像改变了它的类的意思是,如果说你使用的对象能够完全改变它的行为,那么你会觉得这个对象实际上是从别的类实例化而来的,然而,实际上我们是使用组合通过简单引用不同的状态对象来造成类改变的假象。
       状态模式允许一个对象基于内部状态而拥有不同的行为,对象会将行为委托给当前状态对象,通过将每一个状态封装进一个类,我们把以后需要做的任何改变局部化,状态模式和策略模式的区别在于策略模式通常会用行为或者算法来配置对象,而状态模式允许对象随着状态的改变而改变行为。

十一、代理模式

        代理模式为另一个对象提供一个替身或占位符以控制这个对象的访问。
        代理模式为另一个对象提供代表,以便控制客户对对象的访问,管理访问的方式有很多种,远程代理管理客户和远程对象之间的交互,虚拟代理控制访问实例化开销大的对象,保护代理基于调用者控制对对象方法的访问,代理模式有很多变体,例如:缓存代理、同步代理、防火墙代理和写入时复制代理。代理在结构上类似装饰者,但目的不同,装饰者模式为对象加上行为,而代理呢是控制访问,Java内置的代理支持,可以根据需要建立动态代理,并将所有调用分配到所选的处理器,和其他的包装者一样,代理会造成你的设计中类的数目增加。

1 0
原创粉丝点击