设计模式

设计模式原则

1. 封装变化
2.多用组合少用继承(01)【使用组合建立系统具有很大的弹性,不禁可以讲算法族封装成类,更可以“在运行时动态的改变行为”,只要组合的行为对象符合正确的接口标准即可】
3.针对接口编程,不针对实现编程(01)【一种理解即使真实的针对interface的编程,也可理解成利用多态针对超类进行编程,根据具体的情况执行真正的行为,不会绑死在超类上,针对超类进行编程可以更明确的说成变量的声明类型应该是超类型,通常是一个抽象类或者是一个接口,如此只要是具体实现此超类型的类所产生的对象度可以指定给这个变量,也意味着,声明类时不用理会以后执行时的真正对象类型。】    4.为了交互对象之间松耦合设计而努力,即两者可以交互,但不必清楚彼此的细节,并且当改变两种交互对象,或者在其他地方可以复用,对另一种对象无影响。(02)
5.类应该对扩展开放,对修改关闭。(03)
6.要依赖抽象,不要依赖于具体类。(04)【代码里减少对具体类的依赖是个好事,还有一个专有名词叫做依赖倒置原则,其中有几个关键点(并非都要遵从)(1)变量不可以持有具体类的引用(2)不要让类派生自己的具体类(3)不要覆盖基类中已经实现的方法】
7.最少知识原则,只和紧密相关的对象交互。(07)
8.好莱坞原则,低层组件尽量不调用高层组件【父类】,让高层组件来调用低层。(08)

01、策略模式(Strategy)[对象行为型]

策略模式定义了算法族,分别封装起来,让他们各个之间可以相互替换,此模式让算法的变化独立于使用算法的客户之外。

02、观察者模式(Observer)

观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态,他的所有依赖者都会受到通知并自动更新。即一个subject对应多个observer.观察者依赖于主题,只要主题状态一有变化,观察者就会被通知,根据通知的风格,观察者可能因此新值而更新。

javaAPI中内置了观察者者模式,不仅可以发生变化时推送给观察者,还可以观察者自己像主题索取,在java.util中的Observer接口(是类需要继承)与Observable接口(是一个类)。

03、 装饰者模式(Decorator)

装饰者模式动态的将责任附加到对象上,若要拓展功能,装饰者提供了比继承更有弹性的替代方案。【目标即使允许类容易扩展,在不修改现有代码的情况下,就可以搭配新的行为,这样设计更具有弹性可以应对改变,可以接受新的功能来应对改变的需求。注意装饰和被装饰者应该是同样的类型的,即拥有同样的超类。】

04、 工厂模式

工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
* 工厂方法用来出处理对象的创建,并将这样的行为封装在子类中,这样客户程序中关于超类的代码就和子类对象创建代码解耦了。
* 所有工厂模式都用来封装对象的创建。工厂方法模式(Factory Method Pattern)通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。
#### 抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不要明确指定具体类。
####工厂方法与抽象工厂的区别
    * 工厂方法运用的是类的继承,而抽象方法是对象的组合
    * 工厂方法创建对象需要扩展一个类,并且覆盖他的工厂方法,通过子类来创建对象,客户只需要知道所使用的的抽象类型就可以,由子类来决定具体的类型,只负责将客户从具体类型中解耦。而抽象工厂是创建一个产品家族的抽象类型,这个类型的子类定义了产品被产生的方法。所以要使用这个工厂,必须实例化它。
    *    总结:当需要创建产品家族或者想要制造相关产品集合起来时,就使用抽象工厂。当想将客户代码从实例化的具体类中解耦或者目前还不知道将来需要实例化哪些具体类时,就用工厂方法。

05、 单件模式(Singleton pattern)

确保一个类只有一个实例,并提供一个全局访问点。原理即是构造函数私有化,如果想使用到实例对象,那么通过调用类的静态方法getInstance来实现。那么久可以向全局变量一样简单的访问,但是还有它的优点单件可以延迟实例化,并且具有一般类的方法和变量【用来管理共享资源如数据库的连接或者线程池】
    代码示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// NOTE: This is not thread safe!
public class Singleton {
private static Singleton uniqueInstance;//静态私有变量
// other useful instance variables here
private Singleton() {}//私有构造函数
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();//通过类内方法调用私有构造函数产生实例
}
return uniqueInstance;
}
// other useful methods here
}
但上面的代码如果是多线程的可能还存在一些问题,因为多线程可能是不安全的,因为我们需要保证在多线程模式下可以正常工作,不要产生多个uniqueInstance静态实例化对象。所以又有了如下几种考虑。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Singleton {
private static Singleton uniqueInstance;//静态私有变量
// other useful instance variables here
private Singleton() {}//私有构造函数
public static synchronized Singleton getInstance() {//方法加同步修饰
if (uniqueInstance == null) {
uniqueInstance = new Singleton();//通过类内方法调用私有构造函数产生实例
}
return uniqueInstance;
}
// other useful methods here
}
上述这种写法每次调用都要需要同步这个方法,而实际我门只需要第一次执行的时候才需要此方法,那么性能就有点低了,如果不考虑性能,可以接受getInstance的额外负担,那么久可以使用synchronized同步。 如果你非常急切的需要创建单件实例,而又不想延迟实例化那么可以看下如下做法。
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Singleton {
private static Singleton uniqueInstance = new Singleton();//直接创建静态私有变量单件实例
// other useful instance variables here
private Singleton() {}//私有构造函数
public static Singleton getInstance() {
return uniqueInstance;
}
// other useful methods here
}
利用如上方法,我们依赖JVM在加载这个类的时候马上创建此唯一单件实例,这样任何线程访问unniqueInstance静态变量之前,一定会创建此实例。 其实还有更高端的办法是运用双重检查加锁,原理是首先检查是否实例已经创建,如果尚未创建,才进行同步。只有第一次会同步,这也真是我们所期盼的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Singleton {
private volatile static Singleton uniqueInstance;//volatile关键词确保当uniqueInstance变量被初始化成实例时候,多个线程能正确的处理uniqueInstance。
// other useful instance variables here
private Singleton() {}//私有构造函数
public static Singleton getInstance() {
if (uniqueInstance == null) {//检查实例如果不存在就进入同步区块
synchronized(Singleton.class){
if (uniqueInstance == null){//进入同步块后再检查一次
uniqueInstance = new Singleton();//只有第一次才彻底执行这里的代码
}
}
}
return uniqueInstance;
}
// other useful methods here
}

06.命令模式(Comand)封装调用

命令模式将请求封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也可以支持撤销的操作。【命令模式可以将动作的请求者从动作的执行者对象中解耦;在被解耦的两者之间是通命令进行沟通的。命令对象封装了接收者和一个或一组动作;调用者通过调用命令对象的execute()发出请求,这会使接收者的动作被调用;调用者可以接受命令当做参数,甚至在运行时动态进行;命令可以支持撤销,做法是一个undo()来与excute相反执行;命令也可以用来实现日志事务系统】。

07-1.适配器模式(adaper-pattern)

适配器模式将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。

07-2.外观模式(Facede-Pattern)

外观模式提供了一个统一的接口,用来访问子系统中的一群接口,外观定义了一个高层接口,让子系统更容易使用。
适配器模式与外观模式都可以包装许多类,但他们的区别在于适配器的意图是改变接口符合客户的期望,而外观模式的意图是提供子系统的一个简化接口(不禁简化接口,也将客户从组件的子系统中解耦)。【当需要使用一个现有的类而其接口并部分符合你的需求时,就需要使用适配器;当需要简化并统一一个很大的接口或者一群复杂的借口时,使用外观;适配器改变接口以符合客户的期望;外观将客户从一个复杂的子系统中解耦;实现一个适配器的复杂程度视目标接口的大小与复杂度决定;实现一个外观需要将子系统组合进外观,然后将工作委托给子系统执行;适配器有两种形式对象适配器和类适配器,类适配器需要用到多重继承;适配器将一个对象包装起来以改变其接口,装饰者是将一个对象包装起来以增加新的行为和责任,外观将一群对象包装起来以简化其接口。】

08. 模板方法模式(templateMethod)

模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。【模板方法模式为我们提供了一种代码复用的重要技巧;模板方法的抽象类可以定义具体的方法、抽象方法和钩子。抽象方法由子类来实现;钩子是一种方法,他在抽象类中不做任何事,或者做默认的事情,子类可以选择要不要去覆盖它;为了防止子类改变模板方法中的算法,可以把模板方法定义为final;好莱坞原则,应该讲决策权放在高层模块中,以便决定如何以及何时调用低层模块;策略模式和模板方法都是封装算法,但是一个用于组合,一个用于继承。工厂方法可以看做成模板方法的一个特殊版本。】

09.迭代器与组合模式(Iterator)

坚持原创技术分享,您的支持将鼓励我继续创作!