可参考:卡奴达摩:23种设计模式
1_单例
确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
优点:避免频繁地创建新的实例,提升了效率。
//饿汉式(静态常量)[可用]
public class Singleton {
private final static Singleton INSTANCE = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return INSTANCE;
}
}
//懒汉式,效率低
public class Singleton {
private static Singleton singleton;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(singleton==null){
singleton = new Singleton();
}
return singleton;
}
}
//懒汉式,双重检查,推荐
public class Singleton {
private static volatile Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
2_ 组合
将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。
优点:用户就不必关心自己处理的是单个对象还是整个组合结构,这就简化了客户端代码。
3_外观
为子系统中的一组接口提供一个一致的界面,些模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
优点:统一了庞杂的接口,使之方便调用,大大降低耦合。(最少知道drmj)
4_代理(控制访问)
为其他对象提供一种代理以控制对这个对象的访问。
优点:加入一些其它操作,而这些操作的细节都交给代理处理,你只需要集中目标类功能的实现。可以控制访问权限。
![]()
5_迭代器
提供一种方法,实现顺序遍历一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
优点:简化了遍历方式。
6_观察者
定义对象间一对多的依赖关系,当一个对象的状态发生改变时,所有观察它的对象会被通知,并及时更新自己。
优点:一种触发机制,用户无需关心需要改变的对象,更新会自动执行。
7_模板方法( 算法结构 )
定义一个操作中算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变算法的结构即可重定义该算法中的某些特定步骤。
优点:提高代码复用,保证了逻辑框架统一正确。
8_工厂模式:简单工厂模式(也叫静态工厂模式)、工厂方法模式、抽象工厂模式
对象实例化通常是一个很复杂的过程,包含许多类对象的使用。工厂使得a无需关心对象实例过程,依赖工厂即可;b无需关心各个对象的具体实现方法,只调用接口即可;c降低了耦合;
工厂方法:定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。(达到扩展不动原则)
抽象工厂:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
工厂方法 与 抽象工厂:工厂方法模式提供的所有产品都是衍生自同一个接口或抽象类,而抽象工厂模式所提供的产品则是衍生自不同的接口或抽象类。
9_适配器
将一个类的接口转换成客户希望的别外一个接口,使得原来接口不兼容而不能一起工作的那些类可以一起工作。
优点:适配不能使用的类,使之达到利用现存类的目的。
10_装饰(灵活扩展功能)
动态地给一个对象添加一些额外的功能,就增加功能来说,装饰者提供了比继承更有弹性的替代方案。
命令
将一个请求封装为一个对象,从而使你可用不同的请求客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
命令模式的封装性很好:对于客户端来说,需要什么功能就去调用相应的命令,而无需知道命令具体是怎么执行的;其次,命令模式的扩展性很好,代码的复用性很好。比如,文件的操作中,我们需要增加一个剪切文件的命令,则只需要把复制文件和删除文件这两个命令组合一下就行了,非常方便。
状态
当一个对象的内在改变时允许改变其行为,这个对象看起来像是改变了其类。
优点:把状态的判断逻辑转移到表示不同状态的一系列类中,简化复杂的判断逻辑。
策略
定义一组算法,将每个算法都封装起来,并且使他们之间可以互换。
(context作用是什么?为什么要进行二次封装?感觉没意思呀?因为有可能会进行二次封装时要进行特殊处理?)优点:策略实现了同一接口,使用时可以自由切换,运行时动态地决定使用哪一种;易扩展新的策略;
原型
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
优点:通过复制一个已经存在的实例来返回新的实例,而不是新建实例.简单复杂的实例化过程,提高创建效率。
PS:深拷贝与浅拷贝问题中,会发生深拷贝的有java中的8中基本类型以及他们的封装类型,另外还有String类型。其余的都是浅拷贝。
建造者
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
一般产品类和建造者类是比较稳定的,因此,将主要的业务逻辑封装在导演类中对整体而言可以取得比较好的稳定性。
抽象工厂
桥接
职责链
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理为止。
优点:化解if…else中介者
享元
采用一个共享来避免大量拥有相同内容对象的开销。这种开销中最常见、直观的就是内存的损耗。享元模式以共享的方式高效的支持大量的细粒度对象。
解释器
- 访问者
- 备忘录
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态。
简单工厂模式:
public class OperationFactory{ //工厂类 public static Operation createOperate(String operate){ Operation oper = null; switch(operate){ case "+": oper = new OperationAdd(); break; case "-": oper = new OperationSub();\ break; } return oper; } }
客户端代码:
Operation oper; oper = OperationFactory.createOperate("+"); oper.NumberA = 1; oper.NumberB = 2; double result = oper.GetResult();
总结:
A.在运算器例子中,把加减乘除相同的属性和方法抽象出来并建立一个Operation类,然后加减乘除方法分别继承这个类并重写操作方法。最后在OperationFactory工厂类中根据输入的运算方法来实例化对应的对象,并返回之,客户端得到这个对象后,便可执行相应的操作。(在OperationFactory中用Operation父类来引用子类,应用了多态,达到了代码的复用)B.在计费器例子中,把计费方法抽象出来建立抽象类CashSuper,当中只有一个抽象方法public abstract double acceptCash(double money);
abstract class CashSuper{ public abstract double acceptCash(double money); }
然后在把子类分为:正常收费,打折收费,满减活动3类,分别实例不同的方法,同理,再在工厂类CashFactory中根据输入来返回具体的实例。(上例中的父类不是抽象类)
在工厂模式中,增加功能需要,增加一个功能类,再修改对应的工厂类。
优点:各个功能业务都封装起来并独立地分开,有条理地管理各个功能类,保证并个功能类的代码安全。当需要增加功能时增加一个功能类即可,需要修改一个功能时,修改对应的功能类即可;但当业务需要频繁地增加或改动功能时,都要修改工厂类,然后再编译并部署,这样的方式并不好。策略模式:
总结:与工厂模式相比,抽象的父类和具体实现的子类不变,用Context类代替Factory类,Contextru接收功能实现的对象,而不像工厂那样生厂对象,具体的功能对象在客户端里生成。可以用工厂与策略结合,在Context中接收switch的字符串,然后绑定到父类引用。
优点:与纯工厂模式相比,结合后的客户端中只有需要Context类即可,而在纯工厂类中需要知道方法的父类;所以结合后的模式耦合性更低。单一职责原则:
就一个类而言,应该仅有一个引起它变化的原因。
意指在一个功能类中,尽可能地只实现一个功能;一个类如果功能过多,一个功能的变化可能会引起其它功能的能力。
开放-封闭原则:
针对软件中的模块来说,扩展是开放的,更改是封闭的。