12.4 代理模式的扩展
12.4.1 普通代理
在网络上代理服务器设置分为透明代理和普通代理,是什么意思呢?透明代理就是用户不用设置代理服务器地址,就可以直接访问,也就是说代理服务器对用户来说是透明的,不用知道它存在的;普通代理则是需要用户自己设置代理服务器的IP地址,用户必须知道代理的存在。我们设计模式中的普通代理和强制代理也是类似的一种结构,普通代理就是我们要知道代理的存在,也就是类似的GamePlayerProxy这个类的存在,然后才能访问;强制代理则是调用者直接调用真实角色,而不用关心代理是否存在,其代理的产生是由真实角色决定的,这样的解释还是比较复杂,我们还是用实例来讲解。
首先说普通代理,它的要求就是客户端只能访问代理角色,而不能访问真实角色,这是比较简单的。我们以上面的例子作为扩展,我自己作为一个游戏玩家,我肯定自己不练级了,也就是场景类不能再直接new一个GamePlayer对象了,它必须由GamePlayerProxy来进行模拟场景,类图修改如图12-4所示。
![](https://box.kancloud.cn/2016-08-14_57b003629efc0.jpg)
图12-4 普通代理类图
改动很小,仅仅修改了两个实现类的构造函数,GamePlayer的构造函数增加了_gamePlayer参数,而代理角色则只要传入代理者名字即可,而不需要说是替哪个对象做代理。GamePlayer类如代码清单12-10所示。
代码清单12-10 普通代理的游戏者
public class GamePlayer implements IGamePlayer {
private String name = "";
//构造函数限制谁能创建对象,并同时传递姓名
public GamePlayer(IGamePlayer _gamePlayer,String _name) throws Exception{
if(_gamePlayer == null ){
throw new Exception("不能创建真实角色!");
}else{
this.name = _name;
}
}
//打怪,最期望的就是杀老怪
public void killBoss() {
System.out.println(this.name + "在打怪!");
}
//进游戏之前你肯定要登录吧,这是一个必要条件
public void login(String user, String password) {
System.out.println("登录名为"+user + "的用户" + this.name + "登录成功!");
}
//升级,升级有很多方法,花钱买是一种,做任务也是一种
public void upgrade() {
System.out.println(this.name + " 又升了一级!");
}
}
在构造函数中,传递进来一个IGamePlayer对象,检查谁能创建真实的角色,当然还可以有其他的限制,比如类名必须为Proxy类等,读者可以根据实际情况进行扩展。GamePlayerProxy如代码清单12-11所示。
代码清单12-11 普通代理的代理者
public class GamePlayerProxy implements IGamePlayer {
private IGamePlayer gamePlayer = null;
//通过构造函数传递要对谁进行代练
public GamePlayerProxy(String name){
try {
gamePlayer = new GamePlayer(this,name);
} catch (Exception e) {
// TODO 异常处理
}
}
//代练杀怪
public void killBoss() {
this.gamePlayer.killBoss();
}
//代练登录
public void login(String user, String password) {
this.gamePlayer.login(user, password);
}
//代练升级
public void upgrade() {
this.gamePlayer.upgrade();
}
}
仅仅修改了构造函数,传递进来一个代理者名称,即可进行代理,在这种改造下,系统更加简洁了,调用者只知道代理存在就可以,不用知道代理了谁。同时场景类也稍作改动,如代码清单12-12所示。
代码清单12-12 普通代理的场景类
public class Client {
public static void main(String[] args) {
//然后再定义一个代练者
IGamePlayer proxy = new GamePlayerProxy("张三");
//开始打游戏,记下时间戳
System.out.println("开始时间是:2009-8-25 10:45");
proxy.login("zhangSan", "password");
//开始杀怪
proxy.killBoss();
//升级
proxy.upgrade();
//记录结束游戏时间
System.out.println("结束时间是:2009-8-26 03:40");
}
}
运行结果完全相同。在该模式下,调用者只知代理而不用知道真实的角色是谁,屏蔽了真实角色的变更对高层模块的影响,真实的主题角色想怎么修改就怎么修改,对高层次的模块没有任何的影响,只要你实现了接口所对应的方法,该模式非常适合对扩展性要求较高的场合。当然,在实际的项目中,一般都是通过约定来禁止new一个真实的角色,这也是一个非常好的方案。
注意 普通代理模式的约束问题,尽量通过团队内的编程规范类约束,因为每一个主题类是可被重用的和可维护的,使用技术约束的方式对系统维护是一种非常不利的因素。
12.4.2 强制代理
强制代理在设计模式中比较另类,为什么这么说呢?一般的思维都是通过代理找到真实的角色,但是强制代理却是要“强制”,你必须通过真实角色查找到代理角色,否则你不能访问。甭管你是通过代理类还是通过直接new一个主题角色类,都不能访问,只有通过真实角色指定的代理类才可以访问,也就是说由真实角色管理代理角色。这么说吧,高层模块new了一个真实角色的对象,返回的却是代理角色,这就好比是你和一个明星比较熟,相互认识,有件事情你需要向她确认一下,于是你就直接拨通了明星的电话:
“喂,沙比呀,我要见一下×××导演,你帮下忙了!”
“不行呀衰哥,我这几天很忙呀,你找我的经纪人吧……”
郁闷了吧,你是想直接绕过她的代理,谁知道返回的还是她的代理,这就是强制代理,你可以不用知道代理存在,但是你的所作所为还是需要代理为你提供。我们把上面的例子稍作修改就可以完成,如图12-5所示。
![](https://box.kancloud.cn/2016-08-14_57b00362b6e4a.jpg)
图12-5 强制代理类图
在接口上增加了一个getProxy方法,真实角色GamePlayer可以指定一个自己的代理,除了代理外谁都不能访问。我们来看代码,先看IGamePlayer接口,如代码清单12-13所示。
代码清单12-13 强制代理的接口类
public interface IGamePlayer {
//登录游戏
public void login(String user,String password);
//杀怪,这是网络游戏的主要特色
public void killBoss();
//升级
public void upgrade();
//每个人都可以找一下自己的代理
public IGamePlayer getProxy();
}
仅仅增加了一个getProxy方法,指定要访问自己必须通过哪个代理,实现类也要做适当的修改,先看真实角色GamePlayer,如代码清单12-14所示。
代码清单12-14 强制代理的真实角色
public class GamePlayer implements IGamePlayer {
private String name = "";
//我的代理是谁
private IGamePlayer proxy = null;
public GamePlayer(String _name){
this.name = _name;
}
//找到自己的代理
public IGamePlayer getProxy(){
this.proxy = new GamePlayerProxy(this);
return this.proxy;
}
//打怪,最期望的就是杀老怪
public void killBoss() {
if(this.isProxy()){
System.out.println(this.name + "在打怪!");
}else{
System.out.println("请使用指定的代理访问");
}
}
//进游戏之前你肯定要登录吧,这是一个必要条件
public void login(String user, String password) {
if(this.isProxy()){
System.out.println("登录名为"+user+"的用户"+this.name+"登录成功!");
}else{
System.out.println("请使用指定的代理访问");;
}
}
//升级,升级有很多方法,花钱买是一种,做任务也是一种
public void upgrade() {
if(this.isProxy()){
System.out.println(this.name + " 又升了一级!");
}else{
System.out.println("请使用指定的代理访问");
}
}
//校验是否是代理访问
private boolean isProxy(){
if(this.proxy == null){
return false;
}else{
return true;
}
}
}
增加了一个私有方法,检查是否是自己指定的代理,是指定的代理则允许访问,否则不允许访问。我们再来看代理角色,如代码清单12-15所示。
代码清单12-15 强制代理的代理类
public class GamePlayerProxy implements IGamePlayer {
private IGamePlayer gamePlayer = null;
//构造函数传递用户名
public GamePlayerProxy(IGamePlayer _gamePlayer){
this.gamePlayer = _gamePlayer;
}
//代练杀怪
public void killBoss() {
this.gamePlayer.killBoss();
}
//代练登录
public void login(String user, String password) {
this.gamePlayer.login(user, password);
}
//代练升级
public void upgrade() {
this.gamePlayer.upgrade();
}
//代理的代理暂时还没有,就是自己
public IGamePlayer getProxy(){
return this;
}
}
代理角色也可以再次被代理,这里我们就没有继续延伸下去了,查找代理的方法就返回自己的实例。代码都写完毕了,我们先按照常规的思路来运行一下,直接new一个真实角色,如代码清单12-16所示。
代码清单12-16 直接访问真实角色
public class Client {
public static void main(String[] args) {
//定义一个游戏的角色
IGamePlayer player = new GamePlayer("张三");
//开始打游戏,记下时间戳
System.out.println("开始时间是:2009-8-25 10:45");
player.login("zhangSan", "password");
//开始杀怪
player.killBoss();
//升级
player.upgrade();
//记录结束游戏时间
System.out.println("结束时间是:2009-8-26 03:40");
}
}
想想看能运行吗?运行结果如下所示:
开始时间是:2009-8-25 10:45
请使用指定的代理访问
请使用指定的代理访问
请使用指定的代理访问
结束时间是:2009-8-26 03:40
它要求你必须通过代理来访问,你想要直接访问它,门儿都没有,好,你要我通过代理来访问,那就生产一个代理,如代码清单12-17所示。
代码清单12-17 直接访问代理类
public class Client {
public static void main(String[] args) {
//定义一个游戏的角色
IGamePlayer player = new GamePlayer("张三");
//然后再定义一个代练者
IGamePlayer proxy = new GamePlayerProxy(player);
//开始打游戏,记下时间戳
System.out.println("开始时间是:2009-8-25 10:45");
proxy.login("zhangSan", "password");
//开始杀怪
proxy.killBoss();
//升级
proxy.upgrade();
//记录结束游戏时间
System.out.println("结束时间是:2009-8-26 03:40");
}
}
这次能访问吗?还是不行,结果如下所示:
开始时间是:2009-8-25 10:45
请使用指定的代理访问
请使用指定的代理访问
请使用指定的代理访问
结束时间是:2009-8-26 03:40
还是不能访问,为什么呢?它不是真实角色指定的对象,这个代理对象是你自己new出来的,当然真实对象不认了,这就好比是那个明星,人家已经告诉你去找她的代理人了,你随便找个代理人能成吗?你必须去找她指定的代理才成!我们修改一下场景类,如代码清单12-18所示。
代码清单12-18 强制代理的场景类
public class Client {
public static void main(String[] args) {
//定义一个游戏的角色
IGamePlayer player = new GamePlayer("张三");
//获得指定的代理
IGamePlayer proxy = player.getProxy();
//开始打游戏,记下时间戳
System.out.println("开始时间是:2009-8-25 10:45");
proxy.login("zhangSan", "password");
//开始杀怪
proxy.killBoss();
//升级
proxy.upgrade();
//记录结束游戏时间
System.out.println("结束时间是:2009-8-26 03:40");
}
}
运行结果如下:
开始时间是:2009-8-25 10:45
登录名为zhangSan 的用户张三登录成功!
张三在打怪!
张三 又升了一级!
结束时间是:2009-8-26 03:40
OK,可以正常访问代理了。强制代理的概念就是要从真实角色查找到代理角色,不允许直接访问真实角色。高层模块只要调用getProxy就可以访问真实角色的所有方法,它根本就不需要产生一个代理出来,代理的管理已经由真实角色自己完成。
12.4.3 代理是有个性的
一个类可以实现多个接口,完成不同任务的整合。也就是说代理类不仅仅可以实现主题接口,也可以实现其他接口完成不同的任务,而且代理的目的是在目标对象方法的基础上作增强,这种增强的本质通常就是对目标对象的方法进行拦截和过滤。例如游戏代理是需要收费的,升一级需要5元钱,这个计算功能就是代理类的个性,它应该在代理的接口中定义,如图12-6所示。
![](https://box.kancloud.cn/2016-08-14_57b00362cc49a.jpg)
图12-6 代理类的个性
增加了一个IProxy接口,其作用是计算代理的费用。我们先来看IProxy接口,如代码清单12-19所示。
代码清单12-19 代理类的接口
public interface IProxy {
//计算费用
public void count();
}
仅仅一个方法,非常简单,看GamePlayerProxy带来的变化,如代码清单12-20所示。
代码清单12-20 代理类
public class GamePlayerProxy implements IGamePlayer,IProxy {
private IGamePlayer gamePlayer = null;
//通过构造函数传递要对谁进行代练
public GamePlayerProxy(IGamePlayer _gamePlayer){
this.gamePlayer = _gamePlayer;
}
//代练杀怪
public void killBoss() {
this.gamePlayer.killBoss();
}
//代练登录
public void login(String user, String password) {
this.gamePlayer.login(user, password);
}
//代练升级
public void upgrade() {
this.gamePlayer.upgrade();
this.count();
}
//计算费用
public void count(){
System.out.println("升级总费用是:150元");
}
}
实现了IProxy接口,同时在upgrade方法中调用该方法,完成费用结算,其他的类都没有任何改动,运行结果如下:
开始时间是:2009-8-25 10:45
登录名为zhangSan 的用户 张三登录成功!
张三在打怪!
张三 又升了一级!
升级总费用是:150元
结束时间是:2009-8-26 03:40
好了,代理公司也赚钱了,我的游戏也升级了,皆大欢喜。代理类不仅仅是可以有自己的运算方法,通常的情况下代理的职责并不一定单一,它可以组合其他的真实角色,也可以实现自己的职责,比如计算费用。代理类可以为真实角色预处理消息、过滤消息、消息转发、事后处理消息等功能。当然一个代理类,可以代理多个真实角色,并且真实角色之间可以有耦合关系,读者可以自行扩展一下。
12.4.4 动态代理
放在最后讲的一般都是压轴大戏,动态代理就是如此,上面的章节都是一个引子,动态代理才是重头戏。什么是动态代理?动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定代理哪一个对象。相对来说,自己写代理类的方式就是静态代理。本章节的核心部分就在动态代理上,现在有一个非常流行的名称叫做面向横切面编程,也就是AOP(Aspect Oriented Programming),其核心就是采用了动态代理机制,既然这么重要,我们就来看看动态代理是如何实现的,还是以打游戏为例,类图修改一下以实现动态代理,如图12-7所示。
![](https://box.kancloud.cn/2016-08-14_57b00362e58c5.jpg)
图12-7 动态代理
在类图中增加了一个InvocationHandler接口和GamePlayIH类,作用就是产生一个对象的代理对象,其中InvocationHandler是JDK提供的动态代理接口,对被代理类的方法进行代理。我们来看程序,接口保持不变,实现类也没有变化,请参考代码清单12-1和代码清单12-2所示。我们来看DynamicProxy类,如代码清单12-21所示。
代码清单12-21 动态代理类
public class GamePlayIH implements InvocationHandler {
//被代理者
Class cls =null;
//被代理的实例
Object obj = null;
//我要代理谁
public GamePlayIH(Object _obj){
this.obj = _obj;
}
//调用被代理的方法
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result = method.invoke(this.obj, args);
return result;
}
}
其中invoke方法是接口InvocationHandler定义必须实现的,它完成对真实方法的调用。我们来详细讲解一下InvocationHandler接口,动态代理是根据被代理的接口生成所有的方法,也就是说给定一个接口,动态代理会宣称“我已经实现该接口下的所有方法了”,那各位读者想想看,动态代理怎么才能实现被代理接口中的方法呢?默认情况下所有的方法返回值都是空的,是的,代理已经实现它了,但是没有任何的逻辑含义,那怎么办?好办,通过InvocationHandler接口,所有方法都由该Handler来进行处理,即所有被代理的方法都由InvocationHandler接管实际的处理任务。
我们接下来看看场景类,如代码清单12-22所示。
代码清单12-22 动态代理的场景类
public class Client {
public static void main(String[] args) throws Throwable {
//定义一个痴迷的玩家
IGamePlayer player = new GamePlayer("张三");
//定义一个handler
InvocationHandler handler = new GamePlayIH(player);
//开始打游戏,记下时间戳
System.out.println("开始时间是:2009-8-25 10:45");
//获得类的class loader
ClassLoader cl = player.getClass().getClassLoader();
//动态产生一个代理者
IGamePlayer proxy = (IGamePlayer)Proxy.newProxyInstance(cl,new Class[]{IGamePlayer.class},handler);
//登录
proxy.login("zhangSan", "password");
//开始杀怪
proxy.killBoss();
//升级
proxy.upgrade();
//记录结束游戏时间
System.out.println("结束时间是:2009-8-26 03:40");
}
}
很奇怪是吗?不要着急,继续看下去。其运行结果如下:
开始时间是:2009-8-25 10:45
登录名为zhangSan 的用户 张三登录成功!
张三在打怪!
张三 又升了一级!
结束时间是:2009-8-26 03:40
我们还是让代练者帮我们打游戏,但是我们既没有创建代理类,也没有实现IGamePlayer接口,这就是动态代理。别急,动态代理可不仅仅就这么多内容,还有更重要的,如果想让游戏登录后发一个信息给我们,防止账号被人盗用嘛,该怎么处理?直接修改被代理类GamePlayer?这不是一个好办法,好办法如代码清单12-23所示。
代码清单12-23 修正后的动态代理
public class GamePlayIH implements InvocationHandler {
//被代理者
Class cls =null;
//被代理的实例
Object obj = null;
//我要代理谁
public GamePlayIH(Object _obj){
this.obj = _obj;
}
//调用被代理的方法
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result = method.invoke(this.obj, args);
//如果是登录方法,则发送信息
if(method.getName().equalsIgnoreCase("login")){
System.out.println("有人在用我的账号登录!");
}
return result;
}
}
看粗体部分,只要在代理中增加一个判断就可以决定是否要发送信息,运行结果如下:
开始时间是:2009-8-25 10:45
登录名为zhangSan的用户 张三登录成功!
有人在用我的账号登录!
张三在打怪!
张三 又升了一级!
结束时间是:2009-8-26 03:40
太棒了!有人用我的账号就发送一个信息,然后看看自己的账号是不是被人盗了,非常好的方法,这就是AOP编程。AOP编程没有使用什么新的技术,但是它对我们的设计、编码有非常大的影响,对于日志、事务、权限等都可以在系统设计阶段不用考虑,而在设计后通过AOP的方式切过去。既然动态代理是如此诱人,我们来看看通用动态代理模型,类图如图12-8所示。
![](https://box.kancloud.cn/2016-08-14_57b00363063af.jpg)
图12-8 动态代理通用类图
很简单,两条独立发展的线路。动态代理实现代理的职责,业务逻辑Subject实现相关的逻辑功能,两者之间没有必然的相互耦合的关系。通知Advice从另一个切面切入,最终在高层模块也就是Client进行耦合,完成逻辑的封装任务。我们先来看Subject接口,如代码清单12-24所示。
代码清单12-24 抽象主题
public interface Subject {
//业务操作
public void doSomething(String str);
}
其中的doSomething是一种标识方法,可以有多个逻辑处理方法,实现类如代码清单12-25所示。
代码清单12-25 真实主题
public class RealSubject implements Subject {
//业务操作
public void doSomething(String str) {
System.out.println("do something!---->" + str);
}
}
重点是我们的MyInvocationHandler,如代码清单12-26所示。
代码清单12-26 动态代理的Handler类
public class MyInvocationHandler implements InvocationHandler {
//被代理的对象
private Object target = null;
//通过构造函数传递一个对象
public MyInvocationHandler(Object _obj){
this.target = _obj;
}
//代理方法
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//执行被代理的方法
return method.invoke(this.target, args);
}
}
非常简单,所有通过动态代理实现的方法全部通过invoke方法调用。DynamicProxy代码如代码清单12-27所示。
代码清单12-27 动态代理类
public class DynamicProxy<T> {
public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h){
//寻找JoinPoint连接点,AOP框架使用元数据定义
if(true){
//执行一个前置通知
(new BeforeAdvice()).exec();
}
//执行目标,并返回结果
return (T)Proxy.newProxyInstance(loader,interfaces, h);
}
}
在这里插入了较多的AOP术语,如在什么地方(连接点)执行什么行为(通知)。我们在这里实现了一个简单的横切面编程,有经验的读者可以看看AOP的配置文件就会明白这段代码的意义了。我们来看通知Advice,也就是我们要切入的类,接口和实现如代码清单12-28所示。
代码清单12-28 通知接口及实现
public interface IAdvice {
//通知只有一个方法,执行即可
public void exec();
}
public class BeforeAdvice implements IAdvice{
public void exec(){
System.out.println("我是前置通知,我被执行了!");
}
}
最后就是看我们怎么调用了,如代码清单12-29所示。
代码清单12-29 动态代理的场景类
public class Client {
public static void main(String[] args) {
//定义一个主题
Subject subject = new RealSubject();
//定义一个Handler
InvocationHandler handler = new MyInvocationHandler(subject);
//定义主题的代理
Subject proxy = DynamicProxy.newProxyInstance(subject.getClass().
getClassLoader(), subject.getClass().getInterfaces(),handler);
//代理的行为
proxy.doSomething("Finish");
}
}
运行结果如下所示:
我是前置通知,我被执行了!
do something!---->Finish
好,所有的程序都看完了,我们回过头来看看程序是怎么实现的。在DynamicProxy类中,我们有这样的方法:
this.obj=Proxy.newProxyInstance(c.getClassLoader(),c.getInterfaces(),new MyInvocationHandler(_obj));
该方法是重新生成了一个对象,为什么要重新生成?你要使用代理呀,注意c.getInterfaces()这句话,这是非常有意思的一句话,是说查找到该类的所有接口,然后实现接口的所有方法。当然了,方法都是空的,由谁具体负责接管呢?是new MyInvocationHandler(_Obj)这个对象。于是我们知道一个类的动态代理类是这样的一个类,由InvocationHandler的实现类实现所有的方法,由其invoke方法接管所有方法的实现,其动态调用过程如图12-9所示。
![](https://box.kancloud.cn/2016-08-14_57b003631c18f.jpg)
图12-9 动态代理调用过程示意图
读者可能注意到我们以上的代码还有更进一步的扩展余地,注意看DynamicProxy类,它是一个通用类,不具有业务意义,如果我们再产生一个实现类是不是就很有意义了呢?如代码清单12-30所示。
代码清单12-30 具体业务的动态代理
public class SubjectDynamicProxy extends DynamicProxy{
public static <T> T newProxyInstance(Subject subject){
//获得ClassLoader
ClassLoader loader = subject.getClass().getClassLoader();
//获得接口数组
Class<?>[] classes = subject.getClass().getInterfaces();
//获得handler
InvocationHandler handler = new MyInvocationHandler(subject);
return newProxyInstance(loader, classes, handler);
}
}
如此扩展以后,高层模块对代理的访问会更加简单,如代码清单12-31所示。
代码清单12-31 场景类
public class Client {
public static void main(String[] args) {
//定义一个主题
Subject subject = new RealSubject();
//定义主题的代理
Subject proxy = SubjectDynamicProxy.newProxyInstance(subject);
//代理的行为
proxy.doSomething("Finish");
}
}
是不是更加简单了?可能读者就要提问了,这样与静态代理还有什么区别?都是需要实现一个代理类,有区别,注意看父类,动态代理的主要意图就是解决我们常说的“审计”问题,也就是横切面编程,在不改变我们已有代码结构的情况下增强或控制对象的行为。
注意 要实现动态代理的首要条件是:被代理类必须实现一个接口,回想一下前面的分析吧。当然了,现在也有很多技术如CGLIB可以实现不需要接口也可以实现动态代理的方式。
再次说明,以上的动态代理是一个通用代理框架。如果你想设计自己的AOP框架,完全可以在此基础上扩展,我们设计的是一个通用代理,只要有一个接口,一个实现类,就可以使用该代理,完成代理的所有功效。
- 前言
- 第一部分 大旗不挥,谁敢冲锋——6大设计原则全新解读
- 第1章 单一职责原则
- 1.2 绝杀技,打破你的传统思维
- 1.3 我单纯,所以我快乐
- 1.4 最佳实践
- 第2章 里氏替换原则
- 2.2 纠纷不断,规则压制
- 2.3 最佳实践
- 第3章 依赖倒置原则
- 3.2 言而无信,你太需要契约
- 3.3 依赖的三种写法
- 3.4 最佳实践
- 第4章 接口隔离原则
- 4.2 美女何其多,观点各不同
- 4.3 保证接口的纯洁性
- 4.4 最佳实践
- 第5章 迪米特法则
- 5.2 我的知识你知道得越少越好
- 5.3 最佳实践
- 第6章 开闭原则
- 6.2 开闭原则的庐山真面目
- 6.3 为什么要采用开闭原则
- 6.4 如何使用开闭原则
- 6.5 最佳实践
- 第二部分 真刀实枪 ——23种设计模式完美演绎
- 第7章 单例模式
- 7.2 单例模式的定义
- 7.3 单例模式的应用
- 7.4 单例模式的扩展
- 7.5 最佳实践
- 第8章 工厂方法模式
- 8.2 工厂方法模式的定义
- 8.3 工厂方法模式的应用
- 8.4 工厂方法模式的扩展
- 8.5 最佳实践
- 第9章 抽象工厂模式
- 9.2 抽象工厂模式的定义
- 9.3 抽象工厂模式的应用
- 9.4 最佳实践
- 第10章 模板方法模式
- 10.2 模板方法模式的定义
- 10.3 模板方法模式的应用
- 10.4 模板方法模式的扩展
- 10.5 最佳实践
- 第11章 建造者模式
- 11.2 建造者模式的定义
- 11.3 建造者模式的应用
- 11.4 建造者模式的扩展
- 11.5 最佳实践
- 第12章 代理模式
- 12.2 代理模式的定义
- 12.3 代理模式的应用
- 12.4 代理模式的扩展
- 12.5 最佳实践
- 第13章 原型模式
- 13.2 原型模式的定义
- 13.3 原型模式的应用
- 13.4 原型模式的注意事项
- 13.5 最佳实践
- 第14章 中介者模式
- 14.2 中介者模式的定义
- 14.3 中介者模式的应用
- 14.4 中介者模式的实际应用
- 14.5 最佳实践
- 第15章 命令模式
- 15.2 命令模式的定义
- 15.3 命令模式的应用
- 15.4 命令模式的扩展
- 15.5 最佳实践
- 第16章 责任链模式
- 16.2 责任链模式的定义
- 16.3 责任链模式的应用
- 16.4 最佳实践
- 第17章 装饰模式
- 17.2 装饰模式的定义
- 17.3 装饰模式应用
- 17.4 最佳实践
- 第18章 策略模式
- 18.2 策略模式的定义
- 18.3 策略模式的应用
- 18.4 策略模式的扩展
- 18.5 最佳实践
- 第19章 适配器模式
- 19.2 适配器模式的定义
- 19.3 适配器模式的应用
- 19.4 适配器模式的扩展
- 19.5 最佳实践
- 第20章 迭代器模式
- 20.2 迭代器模式的定义
- 20.3 迭代器模式的应用
- 20.4 最佳实践
- 第21章 组合模式
- 21.2 组合模式的定义
- 21.3 组合模式的应用
- 21.4 组合模式的扩展
- 21.5 最佳实践
- 第22章 观察者模式
- 22.2 观察者模式的定义
- 22.3 观察者模式的应用
- 22.4 观察者模式的扩展
- 22.5 最佳实践
- 第23章 门面模式
- 23.2 门面模式的定义
- 23.3 门面模式的应用
- 23.4 门面模式的注意事项
- 23.5 最佳实践
- 第24章 备忘录模式
- 24.2 备忘录模式的定义
- 24.3 备忘录模式的应用
- 24.4 备忘录模式的扩展
- 24.5 最佳实践
- 第25章 访问者模式
- 25.2 访问者模式的定义
- 25.3 访问者模式的应用
- 25.4 访问者模式的扩展
- 25.5 最佳实践
- 第26章 状态模式
- 26.2 状态模式的定义
- 26.3 状态模式的应用
- 第27章 解释器模式
- 27.2 解释器模式的定义
- 27.3 解释器模式的应用
- 27.4 最佳实践
- 第28章 享元模式
- 28.2 享元模式的定义
- 28.3 享元模式的应用
- 28.4 享元模式的扩展
- 28.5 最佳实践
- 第29章 桥梁模式
- 29.2 桥梁模式的定义
- 29.3 桥梁模式的应用
- 29.4 最佳实践
- 第三部分 谁的地盘谁做主 ——设计模式PK
- 第30章 创建类模式大PK
- 30.1 工厂方法模式VS建造者模式
- 30.2 抽象工厂模式VS建造者模式
- 第31章 结构类模式大PK
- 31.1 代理模式VS装饰模式
- 31.2 装饰模式VS适配器模式
- 第32章 行为类模式大PK
- 32.1 命令模式VS策略模式
- 32.2 策略模式VS状态模式
- 32.3 观察者模式VS责任链模式
- 第33章 跨战区PK
- 33.1 策略模式VS桥梁模式
- 33.2 门面模式VS中介者模式
- 33.3 包装模式群PK
- 第四部分 完美世界 ——设计模式混编
- 第34章 命令模式+责任链模式
- 34.2 混编小结
- 第35章 工厂方法模式+策略模式
- 35.2 混编小结
- 第36章 观察者模式+中介者模式
- 36.2 混编小结
- 第五部分 扩展篇
- 第37章 MVC框架
- 37.2 最佳实践
- 第38章 新模式
- 38.1 规格模式
- 38.2 对象池模式
- 38.3 雇工模式
- 38.4 黑板模式
- 38.5 空对象模式
- 附录 23种设计模式彩图