写在前面:设计模式是具体实践的升华,来源于实践,高于实践,并指导实践。
1、建立可维护的OO系统,要诀就在于随时想到系统以后可能需要的变化以及应付变化的原则。
2、大多数的模式和原则,都着眼于软件变化的主题。
3、我们常常把系统中会变化的部分抽离出来进行封装。
理解设计模式,不是具体理解设计模式是怎么实现的,而是理解需要某个设计模式存在的场景,或者说某个设计模式是解决什么场景下的问题,这个就要你具备有“场景”的概念,如果没有“场景”也就无从谈起设计模式,所以最重要的在于你要抽象出场景的概念。
还是拿一个具体的例子来说,简单就拿单例模式来说:
单例模式最初的定义出现于《设计模式》(艾迪生维斯理, 1994):“保证一个类仅有一个实例,并提供一个访问它的全局访问点。”
那么单例模式的场景是什么?
首先我们不考虑单不单例的问题,我们认为所有的类都可以衍生出来多个实例,我要用实例的时候就直接衍生,我们总会发现,我们衍生出来的实例会分成2种类型:
衍生出的实例各个相同
衍生出的实例都有各自不同的特征
那么对于1的类型,我们不仅要问,有必要每次都衍生出新的实例么?可以不可以只衍生一个实例反复使用呢?
ok,单例的场景出来了,就是要避免衍生出很多个相同的实例,当你遇到这个情况时,你就可以使用单例模式设计。其他的设计模式都可以通过类似的还原场景来理解,毕竟先是有问题,后有的解决方案。
建立这个场景的概念是需要不断的实践出来的,还没有场景的概念的时候,就先别说设计模式了。
设计模式的六大原则
1、开闭原则(Open Close Principle):对扩展开放对修改关闭
2、里氏代换原则(Liskov Substitution Principle):父类出现的地方,子类也可出现
3、依赖倒转原则(Dependence Inversion Principle):依赖抽象而不依赖具体
4、接口隔离原则(Interface Segregation Principle):多个隔离的接口,比使用单个接口要好
5、迪米特法则(最少知道原则)(Demeter Principle):最少知道原则。一个实体应当尽量少的与其他实体之间发生相互作用
6、合成复用原则(Composite Reuse Principle):尽量使用合成/聚合的方式,而不是使用继承。
创建型模式(五种)
- 工厂方法模式(Factory):工厂创建对象(经典实现:很多框架初始化时都会创建一个工厂对象,用来加载资源)
- 抽象工厂模式(Abstractfactory):抽象工厂实例创建对象,工厂可修改,灵活度高(经典实现:Struts2插件机制的核心实现就是BeanFactory这个抽象工厂。Spring IOC加载Bean,AOP创建Proxy)
- 单例模式(Sington):适用于只需要一个对象的情况(经典实现:Tomcat中StringManager的错误处理机制)
- 建造者模式(Builder):一步一步创建一个复杂的对象(经典实现:MyBatis中的SQLSession就是结合了Configure,executor等对象,以此来实现SQLSession的复杂功能)
- 原型模式(Prototype):复制对象,包括深度复制和浅度复制,深度复制重建引用对象,浅度复制不创建(经典实现:java序列化)
结构型模式(七种)
- 适配器模式(Adapter):通过实现接口,依赖注入,继承等方式为不相关的实体建立关系(经典实现:Tomcat新版本连接器Coyote,就是通过为Connector适配建立了ProtocolHandler与Tomcat组件Connector的关联关系)
- 装饰器模式(Decorator):创建包装对象修饰扩展被包装对象的功能(经典实现:IO家族中BufferedXxx)
- 代理模式(Proxy):通过添加中间代理的方式限制,过滤,修改被代理类的某些行为(经典实现:Spring AOP核心实现,DataSource中为Connection创建代理对象,改变close方法的行为,使其从开始的关闭连接变成将连接还回连接池)
- 外观模式(Facade):通过外观的包装,使应用程序只能看到外观对象,而不会看到具体的细节对象。(经典实现:Tomcat中创建外观类包装StandardContext传给Wrapper,创建外观类包装Wrapper以ServletConfiguration的形式传给Servlet,以此来屏蔽不想让Servlet可见的那些Tomcat容器参数)
- 桥接模式(Bridge):将抽象部分与它的实现部分分离,使它们都可以独立地变化(经典实现:JDBC驱动)
- 组合模式(Composite):部分与整体,常用于表示树形结构
- 享元模式(Flyweight):维护资源集合(经典实现:数据库连接池,避免重新开启数据库链接的开销)
行为型模式(十一种)
- 策略模式(Strategy):定义多个不同的实现类,这些类实现公共接口,通过调用接口调用不同实例得到不同结果(经典实现:Spring中Bean的定义与注入,Controller,Servcie,repository三层架构中只依赖上一层接口)
- 模板方法模式(Template):父类定义公共方法,不同子类重写父类抽象方法,得到不同结果(经典实现:Tomcat生命周期中的init,SpringIOC上层类加载具体子类指定的配置文件)
- 观察者模式(Observer):目标方法被调用,通知所有观察者(经典实现:Tomcat生命周期事件监听,Spring BeanPostProcessor实现 )
- 迭代子模式(Interator):提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。(经典实现:集合迭代器)
- 责任链模式(ChainOfResponsibility):链式依赖,依次调用(经典实现:Tomcat Valve)
- 命令模式(Commond):Action定义具体命令,拦截器Invocation回调执行命令(经典实现:Struts2)
- 备忘录模式(Memento):建立原始对象副本,用于存储恢复原始对象数据
- 状态模式(Stage):通过改变状态,改变行为(经典实现:切换装载着不同配置信息的配置文件对象)
- 访问者模式(Visitor):结构与操作解耦。灵活的操作,放入固定的结构中执行(经典实现:在SpringAOP的实现过程中首先会有一个ProxyCreator去创建切入点,通知之类的,然后创建一个抽象工厂将这些参数对象传递给抽象工厂,抽象工厂调用createAopProxy(this)来创建对象,传入不同的抽象工厂创建出不同的实体对象)
- 中介者模式(Mediator):MVC 框架,其中C(控制器)就是 M(模型)和 V(视图)的中介者
- 解释器模式(Iterpreter):定义分别定义 + - * / 非终结符,组合不同的非终结符定义不同的表达式,维护繁琐
最近又看了一遍java23种设计模式,网上很多都说java有23种设计模式,但是总感觉不对,像拦截器,监听器,过滤器,mvc难道不属于设计模式吗。这个问题没多大意义,就不讨论了。上次直接把这23种设计模式看完之后当时看小Demo貌似是理解了,但是怎么也不能把23种全部背下来。最近又重新看了一遍,认真的体会了一下其中的思想。其实说这句话有些牵强了,现在的水平再怎么认真也不能把设计模式的思想体会的淋漓尽致。这次与上次的不同是,几乎不用看讲解了,看代码全部能理解小Demo的工作原理。于是试着各用一句话来总结他们。无论是深入理解设计模式的大牛,还是没看过设计模式的初学者。一定会有许多人看到我的总结不知所云。我只是试着按我的思路总结一下。好让自己容易记忆。各位也同样可以按自己的思路各写一句话总结。
如果想快速了解一下设计模式推荐一篇博客:http://www.cnblogs.com/maowang1991/archive/2013/04/15/3023236.html。对的,是了解,不是研究。个人感觉设计模式并不是随随便便给你举个现实中的例子就能够学好的。即使例子再生动,想理解设计模式的精髓必须深入到代码中去。例如大多数框架开启时用到的工厂模式;Tomcat中Pipeline中Valve的责任链模式调用;StandardBase中使用代理模式引入LifecycleSupport来增加自身对生命周期的管理能力;LifecycleListener中执行各个监听器时的观察者模式。
个人认为设计模式的学习非一朝一夕能成的东西。它是在自己做开发看源码的工程中慢慢体会出来的。写个小案例,告诉你这叫XX模式,像这种帖子网上已经很多了,没必要再重复了,之后在读源码的工程中遇到好的应用一定会总结到博客中的。
提供一个自己学习设计模式的思路:先看小Demo认识各种设计模式的样子,之后在读源码时再慢慢理解各种设计模式的好处。而且设计模式往往还不是单独一个出现的,例如过滤器加拦截器能实现SpringAOP的功能。知识有限能举的例子也不多,读源码时注意这一点就行。
如果想看Demo可以在网上找,也可以在我github网址(https://github.com/smallbug-vip/repo)上获取,其中大部分源码还是从上面博客中直接拷贝的。
------------------------------------------------------------------------------------------------------------------------------------------------------------------
以下是正文:
策略模式
一、策略模式:通过统一(抽离)一个行为接口出来,针对这个接口有不同的实现算法族,这些算法之间可以互相替换,该模式让算法独立于使用它的客户而独立变化。这里的策略就是指我们到底选用算法族中的哪一个算法。
针对接口编程,而不是针对实现编程。
—抽象策略角色: 策略类,通常由一个接口或者抽象类实现。
—具体策略角色:包装了相关的算法和行为。
—环境角色:持有一个策略类的引用,最终给客户端调用。
策略模式的使用场景:
(1)针对同一类型问题,有多种具体的处理方式时。 即多个类只区别在表现行为不同,在运行时动态选择具体要执行的行为。并且还可能在未来用其它方式来实现。
(2)需要对客户隐藏具体策略(算法)的实现细节时。
(3)有大量if-else if – … - else或者switch – case … , 并且这些不同的操作属于同一类型的操作,只是具体的处理方式不同时,就可以使用策略模式。
策略模式角色介绍:
(1)Stragety类:策略的抽象类或接口类。所有的具体策略类都有一些公有的行为,这些公有的行为被抽象到Strategy类里。
(2)ConcreteStragety类:具体的策略实现类。
(3)Context类:持有一个策略类引用(选择使用哪一种策略)的上下文环境。
策略模式的优缺点
策略模式的优点:
(1)结构清晰,降低了耦合度,易于【拓展】。上例中如果需要再添加一种出行方式,只需要修改较少的已有代码。
(2)对客户隐藏具体策略(算法)的实现细节,封装性更好,数据更安全。
策略模式的缺点:
随着策略数量的增加,具体实现子类会变得越来越多。
优点:
提供了一种替代继承的方法,而且保留了继承的优点,比继承更独立(算法独立,更容易扩展)
避免程序使用多重转移语句,使系统更加灵活,易于扩展。
遵守大部分设计原则,高内聚,低耦合
缺点:
每个具体策略都会产生一个新类,所以会增加维护类的数量。
例子:
使用策略模式解决问题
1、 Stragety接口类
[java] view plain copy
public interface Stragety{
int calculateMoney(int distance);
}
2、 Stragety接口实现类
[java] view plain copy
/*
*@author SEU_Calvin
*@date 2016/09/06
*/
public class TaxiStragety implements Stragety{
@override
public int calculateMoney(int distance){
//出租车具体计价算法,参数为出行距离
}
}
public class SubwayStragety implements Stragety{
@override
public int calculateMoney(int distance){
//地铁具体计价算法,参数为出行距离
}
}
3、 Context类
[java] view plain copy
/*
*@具体使用类
*@author SEU_Calvin
*@date 2016/09/06
*/
public class Test{
public static void main(String[] args){
Test calculator = new Test();
//设置出租车出行策略
calculator.setStrategy(new TaxiStrategy);
//计算价格
System.out.println("出行20公里的费用为==>" + calculator. calculateMoney(20));
}
Stragety mStragety;
public void setStrategy(Stragety mStragety){
this.mStragety = mStragety;
}
public int calculateMoney (int distance){
return mStragety. calculateMoney(distance);
}
}
总结:
策略模式:
对于同一种行为的不同表现(实现)形式,这些不同的表现形式就是一个个的策略,针对这些策略类我们可以抽取出其共性并名为接口,在c/c++中我们灵活使用策略类的场景总结:
(1)多态,定义一个抽象类接口,子类实现这些接口。
(2)对同一种行为的多种实现,比如排序:有插入排序、冒泡排序、快速排序等,可以统一出一个行为接口为排序,具体选用哪一个排序,根据 实际需求。
C语言例子:
- int add(int,int);
- //设置默认策略
- int (*p)(int,int)=add;
- //策略环境
- static void environment(int (*x)(int,int)){
- p=x;
- }
- //抽象策略
- int strategy(int x,int y){
- return (*p)(x,y);
- }
- //加法策略
- int add(int x,int y){
- return x+y;
- }
- //减法策略
- int sub(int x,int y){
- return x-y;
- }
- //乘法策略
- int multiply(int x,int y){
- return x*y;
- }
- //除法策略
- int divide(int x,int y){
- return x/y;
- }
- --------------------------------------------以下为策略模式测试代码:
- #include"strategy.h"
- #include<stdio.h>
- //自定义策略
- int mystrategy(int x,int y){
- return 2*x-y;
- }
- void main(){
- //使用默认策略计算
- printf("%d\n",strategy(6,4)); //将减法策略放到策略环境中
- environment(sub);
- printf("%d\n",strategy(6,4));
- //将乘法法策略放到策略环境中
- environment(multiply);
- printf("%d\n",strategy(6,4)); //将除法策略放到策略环境中
- environment(divide);
- printf("%d\n",strategy(6,4)); //将自定义策略放到策略环境中
- environment(mystrategy);
- printf("%d\n",strategy(6,4));
- }