代理模式,装饰模式,适配器模式

代理模式只是原来对象的一个替身,装饰模式是对原对象的功能增强,适配器模式是要改变原对象的接口

 

 

Structural patterns
Adapter 适配
allows classes with incompatible interfaces to work together by wrapping its own interface around that of an already existing class.

Bridge 桥梁
decouples an abstraction from its implementation so that the two can vary independently.

Composite
composes zero-or-more similar objects so that they can be manipulated as one object.

Decorator 装饰
dynamically adds/overrides behaviour in an existing method of an object.

Facade
provides a simplified interface to a large body of code.

Flyweight
reduces the cost of creating and manipulating a large number of similar objects.

Proxy 代理
provides a placeholder for another object to control access, reduce cost, and reduce complexity.

 

合成,门户和享元相对独立,比较好分辨。

 

这里仅仅对适配,装饰和代理进行辨析。

 

适配的目的是匹配两个不同的接口。

装饰的目的是增强接口的实现。

代理的目的是有代理对象控制原对象,不改变接口。

 

这里给出例子

 

假设有饭店和咖啡厅两个接口。我们希望实现了咖啡厅的类,可以去做饭店的事情,就是适配,增强咖啡厅自身功能就是装饰,对咖啡连锁供应商的调度就是代理。

 

 

package mars.designpattern.info;

public interface 饭店 {

    void 提供饭菜();
   
    void 提供啤酒();
}

 

package mars.designpattern.info;

public interface 咖啡厅 {

    void 提供西点();

    void 提供咖啡();

    void 提供音乐();

    void 提供上网();
}

 

package mars.designpattern.structural;

import mars.designpattern.info.咖啡厅;
import mars.designpattern.info.饭店;
import mars.designpattern.info.impl.星巴克;

public class 适配 implements 饭店 {
    private 咖啡厅 中关村店 = new 星巴克();

    @Override
    public void 提供饭菜() {
        中关村店.提供西点();
        中关村店.提供咖啡();
    }

    @Override
    public void 提供啤酒() {
        // 这个真没有
    }
}

 

package mars.designpattern.structural;

import java.util.Queue;

import mars.designpattern.info.咖啡厅;

public class 装饰 implements 咖啡厅 {

    private 咖啡厅 基础店 = new 星巴克();
    
    private void 提供苹果笔记本() {
        System.out.println("苹果笔记本 2小时内免费租用");
    }

    private void 咖啡杯服务() {
        System.out.println("根据会员资料,免费提供一个12星座咖啡杯,并免费帮助寄存");

    }
   
    private void 点播服务(){
        System.out.println("提供有偿点播服务");
    }
   
    private Queue 订餐;
    public void 订餐(){
        System.out.println("提供订餐电话");  
        订餐.add("订餐信息");
    }
   
    private void 提供外卖(){
        System.out.println("根据订餐信息 免费送货");
    }

    @Override
    public void 提供上网() {
            基础店.提供上网();
        提供苹果笔记本();
        System.out.println("无线宽带");

    }

    @Override
    public void 提供咖啡() {
        基础店.提供咖啡();
        System.out.println("手工研磨");
        咖啡杯服务();
       
        if(订餐.remove("订餐信息")){
            提供外卖();
        }
    }

    @Override
    public void 提供西点() {
            基础店.提供西点();
    }

    @Override
    public void 提供音乐() {
        基础店.提供音乐();
        点播服务();
    }
}

 

package mars.designpattern.structural;

import mars.designpattern.info.咖啡厅;
import mars.designpattern.info.impl.星巴克;

public class 代理 implements 咖啡厅 {
    星巴克 星巴克注册者;
   
    private void 记账(){
       
    }
   
    @Override
    public void 提供上网() {
        星巴克注册者.提供上网();
    }

    @Override
    public void 提供咖啡() {
        星巴克注册者.提供咖啡();
        记账();
    }

    @Override
    public void 提供西点() {
        星巴克注册者.提供西点();
        记账();
    }

    @Override
    public void 提供音乐() {
        星巴克注册者.提供音乐();
    }
}

 

设计模式 适配器模式

适配器模式和代理模式是二个比较容易混淆的模式,我想谈一下这两个模式的差别,不过我先分别介绍一下这二个模式,然后再做比较,我想这样大家可能会比较容易理解,对于不懂这两个模式的人来说也多个了解的机会。

适配器说通俗点就是把一个东西包装一下变成另外一个东西,为什么要包装而不直接就用这个东西呢?呵呵,如果能直接用就犯不着适配了,要适配当然就是由于某些原因你用不了当前这个东西。最容易理解的就是电器的例子,比如你在中国买的电器使用电压是 220V ,结果你跑到国外去了,国外提供的电压是 110V ,问题就来了,你必须使用的是 220V 的,国外提供给你的却只有 110V 的,所以你根本就用不了,除非你能够将 110V 的转化成为 220V 才能行。此时适配器就排上用场了,你使用一个转压器不就可以把 110V 专成 220V 了吗?对于程序设计亦然。

         下面举一个例子,假如你开始做一个项目的时候你需要写一个集合的枚举接口如下:

public interface Enumeration

     {

         bool HasMoreElement();

         object nextElement();

     }

       调用的方法如下:

public void BeforeDisplay(Enumeration e)

     {

         while (e.HasMoreElement())

         {

              e.nextElement();

         }

     }

       后来由于某一种原因(可能是代码重构、或者类库升级),你定义了另外一个新的集合枚举接口如下:

public interface Iterator

     {

         bool MoveNext();

         object Current();

         void Reset();

     }

       使用新的接口调用方法如下:

public void OldDisplay(Iterator e)

     {

         while (e.MoveNext())

         {

              e.Current();

         }

     }

     现在如果要求新的方法都只使用新的接口一切都很顺利,但是由于原来的很多方法用的仍然是旧的接口,而由于逻辑的重用或者 为了升级后类库的向前兼容,你需要将当前的新接口Iterator 转换成旧的接口Enumeration 以调用原来的方法,这时候就需要我们写一个适配器。

     如何编写适配器?由于我们需要用的是Enumeration 接口,所以适配器必须是Enumeration 的,即必须继承自Enumeration ;接着想我们需要利用新的Iterator 做业务,简单点就是我们需要用Iterator 中的方法,所以最容易想到的一个方法就是拥有一个Iterator 的实例,直接调用该实例的方法实现Enumeration 接口不就可以了吗?(如果Iterator 是个具体的类,我们还可以再继承该类,来个双重继承,利用该具体类的方法实现Enumeration 接口,效果一致)

     示例代码如下:

public class Itermeration:Enumeration

     {

         private Iterator it;

         public Itermeration(Iterator it)

         {

              this .it=it;

         }

 

         public bool HasMoreElement()

         {

              return it.MoveNext();

         }

 

         public object nextElement()

         {

              return it.Current();

         }

     }

     注意:由于我们需要将新接口Iterator 转换成旧的接口Enumeration ,所以新的类必须继承旧的接口Enumeration ,将传入的Iterator 转换成Enumeration 接口。这样调用方法如下:

public void Display(Iterator e)

     {

         BeforeDisplay(new Itermeration(e));

           // 此处将Iterator 转化为Itermeration ,而Itermeration 又是继承自Enumeration ,所以实际上就是将Iterator 转化为Enumeration

     }

     注意:我们为了将Iterator 转化成Enumeration ,采用的方式是在他们之间增加了一层,使用Itermeration 类来转化,这是面向对象设计中抽象的一个主要思想,贯穿很多设计模式。

     适配器模式有两种,我现在举的这个例子是对象的适配器模式,还有一种是类的适配器模式( 对应上边括弧中的注释) ,这两种方式网上介绍的已经太多,我这里就不再赘述,当然现实中以对象的适配器模式使用最多,这也是为什么我举这种类型例子的原因。

     其实适配器的思想是很容易理解的,很多人觉得困惑的原因常常不是他们不知道什么是适配器,而是他们联系自己曾有的经验,找不到要这么做的理由,所以内心深处总是对这么做感觉疑惑,当然很多其他的模式也是如此。

     那到底什么时候需要用适配器呢?一个根本的理由就是系统需要使用现有的类,但是这个类的接口不符合当前的要求。

     注 意我这里说的是根本理由,因为这个理由是适配器出现的致命原因,由于这个理由出现了适配器,然后我们仔细揣摩出现后的适配器突然就发现他还有其它的好处, 也就冒出了其它的理由。我强调这个的主要原因就是因为很多人开始看一篇文章描述觉得好像是这么回事,结果又看其它人的文章反而觉得越来越迷糊,实际上很大 程度上的原因是很多文章往往为了强调全面而花了很多笔墨去写那些后来想起来的好处,而对根本理由轻轻带过,导致很多人误以为导出这种模式的原因是为了使用 后来这些好处,结果就越搅越迷糊了。

     这里我给出另外一个使用理由,如果你看不太懂没有关系,完全可以忽略不看。

     另一个理由就是我们系统中有很多彼此不大关联的类,而且这些类的接口都非常简单,(注意是接口都非常简单,如果都比较复杂用这种方法就是没事找事)。现在我想建立一个可以重复使用的类来跟这些简单类打交道,于是就使用适配,个人认为纯粹是为了方便。

 

设计模式 代理模式

         代理这个词大家肯定已经非常熟悉,因为现实中接触的很多,其实现实中的东西恰恰可以非常形象和直观地反映出模式的抽象过程以及本质。现在房子不是吵得热火朝天吗?我们就以房子为例,来拨开代理的面纱。

         假 设你有一套房子要卖,一种方法是你直接去网上发布出售信息,然后直接带要买房子的人来看房子、过户等一直到房子卖出去,但是可能你很忙,你没有时间去处理 这些事情,所以你可以去找中介,让中介帮你处理这些琐碎事情,中介实际上就是你的代理。本来是你要做的事情,现在中介帮助你一一处理,对于买方来说跟你直 接交易跟同中介直接交易没有任何差异,买方甚至可能觉察不到你的存在,这实际上就是代理的一个最大好处。

         接下来我们再深入考虑一下为什么你不直接买房子而需要中介?其实一个问题恰恰解答了什么时候该用代理模式的问题。

         原因一:你可能在外地上班,买房子的人没法找到你直接交易。

         对应到我们程序设计的时候就是:客户端无法直接操作实际对象。那么为什么无法直接操作?一种情况是 你需要调用的对象在另外一台机器上,你需要跨越网络才能访问,如果让你直接 coding 去 调用,你需要处理网络连接、处理打包、解包等等非常复杂的步骤,所以为了简化客户端的处理,我们使用代理模式,在客户端建立一个远程对象的代理,客户端就 象调用本地对象一样调用该代理,再由代理去跟实际对象联系,对于客户端来说可能根本没有感觉到调用的东西在网络另外一端,这实际上就是 Web Service 的工作原理。另一种情况 虽然你所要调用的对象就在本地,但是由于调用非常耗时,你怕影响你正常的操作,所以特意找个代理来处理这种耗时情况,一个最容易理解的就是 Word 里面装了很大一张图片,在 word 被打开的时候我们肯定要加载里面的内容一起打开,但是如果等加载完这个大图片再打开 Word 用户等得可能早已经跳脚了,所以我们可以为这个图片设置一个代理,让代理慢慢打开这个图片而不影响 Word 本来的打开的功能。申明一下我只是猜可能 Word 是这么做的,具体到底怎么做的,俺也不知道。

         原因二:你不知道怎么办过户手续,或者说除了你现在会干的事情外,还需要做其他的事情才能达成目的。

         对 应到我们程序设计的时候就是:除了当前类能够提供的功能外,我们还需要补充一些其他功能。最容易想到的情况就是权限过滤,我有一个类做某项业务,但是由于 安全原因只有某些用户才可以调用这个类,此时我们就可以做一个该类的代理类,要求所有请求必须通过该代理类,由该代理类做权限判断,如果安全则调用实际类 的业务开始处理。可能有人说为什么我要多加个代理类?我只需要在原来类的方法里面加上权限过滤不就完了吗?在程序设计中有一个类的单一性原则问题,这个原 则很简单,就是每个类的功能尽可能单一。为什么要单一,因为只有功能单一这个类被改动的可能性才会最小,就拿刚才的例子来说,如果你将权限判断放在当前类 里面,当前这个类就既要负责自己本身业务逻辑、又要负责权限判断,那么就有两个导致该类变化的原因,现在如果权限规则一旦变化,这个类就必需得改,显然这 不是一个好的设计。

         好了,原理的东西已经讲得差不多了,要是再讲个没完可能大家要扔砖头了。呵呵,接下来就看看怎么来实现代理。

代理模式的实现:

         其实代理模式还是很容易实现的,随便举个例子,比如你有一个类负责返回员工的薪资信息,如下:

         public class BusinessClass

     {

         public double GetPayroll(string employee)

         {

              // 返回薪资结果

              return 1000;

         }

     }

         由于薪资信息是公司的机密信息,不是谁都能调用查看,所以我们为该类做一个代理来做用户身份的验证,代码如下:

         public class Proxy

     {

         private BusinessClass bc;

         public Proxy(BusinessClass bc)

         {

              this .bc=bc;

         }

 

         public double GetPayroll(string user)

         {

              // 判断user 权限

              // 如果不符合返回null, 或者抛出异常

              if ( IsManage(user) )

              {

                   return bc.GetPayroll(" 张三");

              }

             

              throw new Exception(" 你没有该权限。");

         }

     }

         注意:代理类需要使用被代理类来做业务逻辑,所以代理类需要包含被代理类的实例,这跟适配器模式是一样的。

         到目前为止目的其实已经达到了,但是现实中我们常常会再为代理类和被代理类抽象出一个公共接口,如下:

         public interface IBusinessClass

     {

         double GetPayroll(string user);

     }

     很多人肯定会问为什么要抽象这个接口呢?其实抽象接口有一个最大的原因就是约束双方的行为!什么意思呢?其实就是我逼迫Proxy 必 须实现某些方法,而这些方法恰恰是对外公开的主要业务方法。当然也可以靠程序员自律,但是多一个约束总归是好的,至少如果没有实现指定方法我们可以在编译 期就发现错误,总比执行时才能发现错误要好。另外的原因可能都得归结到使用接口的好处上去了,这里不再赘述,自己去查接口的文章介绍了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值