对设计模式的总结之装饰模式与代理模式

前言

面向对象编程追求的本质-提高扩展性、可维护性、灵活性和复用性。合理利用面向对象6个原则,能够很好的达到要求。如何利用好就是至关重要的了,前人总结了23+个设计模式能够让初学者更容易学到其中的精髓,本文就说说我对本人对装饰模式与代理模式的见解。

设计模式链接

  1. 对设计模式的总结之原则
  2. 对设计模式的总结之简单工厂与策略模式
  3. 对设计模式的总结之装饰模式与代理模式

装饰模式与代理模式

装饰模式

项目中要实现某个功能,前提条件是:1、该功能由许多单一功能的小功能组合而成;2、该功能中需要实现可灵活改变内部小功能的执行顺序;3、该功能内部的小功能不是固定的;4、用面向对象设计编程。

就比如装修房子:首先房子整体风格有多种,什么欧式风格,田园风格,地中海风格。每种风格中所用的家具种类和家具数量也不一样。要实现这个装修功能,咋个整啦?

有人会马上想到:继承,先建立两个抽象类(选择装修风格抽象和家具装修抽象)。具体风格类继承装修风格抽象类,具体家具类继承家具装修抽象类,客户端先选用具体风格,在更具具体风格去调用具体家具装饰。这样子看起来还不错,但是当风格和家具多了,而且每种风格的家具又是可变的,后期扩展又都会去更改抽象就很麻烦。

有处理这种特殊例子的好办法吗?有,那就是装饰者模式。在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

基本用法

  1.     /// <summary>
        /// 房屋
        /// </summary>
        public abstract class Home
        {
            /// <summary>
            /// 装修抽象
            /// </summary>
            /// <returns></returns>
            public abstract string Decoration();
        }
    
        /// <summary>
        /// 装饰抽象类,要让装饰完全取代抽象组件,所以必须继承自Home
        /// </summary>
        public abstract class Decorator:Home
        {
            private Home home;
    
            public Decorator(Home home)
            {
                this.home = home;
            }
    
            /// <summary>
            /// 装饰抽象
            /// </summary>
            /// <returns></returns>
            public override string Decoration()
            {
                string result = string.Empty;
                if (home!=null)
                {
                    result= home.Decoration();
                }
                return result;
            }
        }
    
        /// <summary>
        /// 北欧风格
        /// </summary>
        public class HomeOfEuropeanStyle:Home
        {
            /// <summary>
            /// 北欧风格装修
            /// </summary>
            /// <returns></returns>
            public override string Decoration()
            {
                return "北欧风格装修(特有的操作)开始:\r\n1、相关的设计师设计出图纸;2、北欧风基础装修。\r\n";
            }
        }
    
        /// <summary>
        /// 田园风
        /// </summary>
        public class HomeOfCountrysideStyle:Home
        {
            /// <summary>
            /// 田园风装修
            /// </summary>
            /// <returns></returns>
            public override string Decoration()
            {
                return "田园风格装修(特有的操作)开始:\r\n1、相关的设计师设计出图纸;2、田园风基础装修。\r\n";
            }
        }
    
        /// <summary>
        /// 沙发
        /// </summary>
        public class FurnitureOfSofa:Decorator
        {
    
            public FurnitureOfSofa(Home home) :base(home){}
    
            /// <summary>
            /// 具体装饰
            /// </summary>
            /// <returns></returns>
            public override string Decoration()
            {
                string result = base.Decoration();
    
                return result+ CutisVeraSofa();
            }
    
            /// <summary>
            /// 安装真皮沙发
            /// </summary>
            /// <returns></returns>
            private string CutisVeraSofa()
            {
                return "\r\n给装修的房子选择真皮沙发。";
            }
        }
    
        /// <summary>
        /// 饭桌
        /// </summary>
        public class FurnitureOfDinningTable : Decorator
        {
            public FurnitureOfDinningTable(Home home):base(home){ }
    
            /// <summary>
            /// 具体饭桌装饰
            /// </summary>
            /// <returns></returns>
            public override string Decoration()
            {
                string result = base.Decoration();
    
                return result + SolidWoodTablt();
            }
    
            /// <summary>
            /// 安装实木餐桌
            /// </summary>
            /// <returns></returns>
            private string SolidWoodTablt()
            {
                return "\r\n给装修的房子选择实木餐桌。";
            }
        }

总结

问题:为什么装饰者模式里面有继承,为什么又不用啦?

回答:这就是装饰者模式一大优点,这里抽象只做类型规范作用,不实现方法的复制。装饰者和被装饰者是同一个类型,这样被装饰者拥有了装饰者独有的功能。这样子我们可以在任何时候,给新的装饰者增加新的行为。如果是用继承,每当需要增加新的行为时,就要修改原程序的基类了。

优缺点:

  1. 被装饰者和装饰者继承同一个基类,客户端可以用一种方式与装饰者和被装饰者交互。
  2. 装饰者和被装饰者是同一个类型,这样被装饰者拥有了装饰者独有的功能。
  3. 装饰者模式是用继承对象规定类型,不做方法复制,提高了灵活性。
  4. 客户端可以随意选择装饰者,组合成个数、排列不同的组合,提高了灵活性。
  5. 增加了提高了编码复杂度。

使用场景:

  1. 需要扩展一个类的功能,或给一个类添加附加职责。
  2. 需要灵活动态添加/排序/减少功能。
  3. 整个功能不同组合都会生成大类子类的情况。

代理模式

毕业季到了,许多同学也将结束啃老族的生活,步入社会的大家庭自力更生-找工作。在以前,同学们就是频繁的进出各个人才招聘市场,每天都累成死狗一样,最后效果还不咋滴。近年来,招聘中介网步入人才市场,很好的解决了招聘单位和被招聘者费时费力却都不如意的问题。

在软件设计中,由于某些情况,客户端不想或不能直接访问一个对象,此时可以通过一个称为“代理”的角色来实现间接访问,该方案对应的设计模式被称为代理模式(给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问)。代理模式是一种应用很广泛的结构型设计模式,而且变化形式非常多,常见的代理形式包括远程代理、保护代理、虚拟代理、缓冲代理、职能引用代理等。

代理模式分类:

  1. 远程代理(Remote Proxy):为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中(eg:WebService,WCF之类的)。
  2. 虚拟代理(Virtual Proxy):如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建(eg:浏览器分阶段载入信息,先文字再图片)。
  3. 保护代理(Protect Proxy):控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限(eg:多级权限系统)。
  4. 缓冲代理(Cache Proxy):为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果(eg:访问频繁时用的缓冲机制)。
  5. 智能引用代理(Smart Reference Proxy):当一个对象被引用时,提供一些额外的操作,例如将对象被调用的次数记录下来等。

基本用法

业务需求:系统中需要一个角色与各种游戏服务端交互的功能,在交互之前必须回去权限,在交互之后要统计交互信息。

  1. /// <summary>
        /// 与游戏端交互抽象
        /// </summary>
        public abstract class Interaction
        {
            /// <summary>
            /// 与游戏交互抽象方法
            /// </summary>
            /// <param name="sLoginName">账号</param>
            /// <param name="AppId">游戏AppId</param>
            /// <returns></returns>
            public abstract string InteractionWithGame(string sLoginName,string AppId);
        }
        /// <summary>
        /// 真实的与游戏交互类
        /// </summary>
        public class RealInteraction: Interaction
        {
            /// <summary>
            /// 与游戏交互
            /// </summary>
            /// <param name="sLoginName"></param>
            /// <param name="AppId"></param>
            /// <returns></returns>
            public override string InteractionWithGame(string sLoginName, string AppId)
            {
                return sLoginName+$"正在与游戏:{AppId}交互...";
            }
        }
        /// <summary>
        /// 与游戏交互代理
        /// </summary>
        public class ProxyInteraction:Interaction
        {
            /// <summary>
            /// 真实的与游戏交互变量
            /// </summary>
            private RealInteraction realInteraction=new RealInteraction();
    
            /// <summary>
            /// 权限验证变量
            /// </summary>
            private Validator validator;
    
            /// <summary>
            /// 统计功能比那辆
            /// </summary>
            private Statistics statistics;
    
            /// <summary>
            /// 代理与游戏交互
            /// </summary>
            /// <param name="sLoginName"></param>
            /// <param name="AppId"></param>
            /// <returns></returns>
            public override string InteractionWithGame(string sLoginName, string AppId)
            {
                //如果身份验证成功,则执行交互
                if (this.Validate(sLoginName))
                {
                    //调用真实主题对象的交互方法
                    string result = realInteraction.InteractionWithGame(sLoginName, AppId);
    
                    //交互统计
                    this.StatisticsInfo(sLoginName, AppId); 
    
                    return result;
                }
                else
                {
                    return null;
                }
            }
    
            /// <summary>
            /// 创建访问验证对象并调用其Validate()方法实现身份验证获取权限
            /// </summary>
            /// <param name="userId"></param>
            /// <returns></returns>
            private bool Validate(string userId)
            {
                validator = new Validator();
                return validator.Validate(userId);
            }
    
            /// <summary>
            /// 交互统计
            /// </summary>
            /// <param name="sLoginName"></param>
            /// <param name="AppId"></param>
            private void StatisticsInfo(string sLoginName, string AppId)
            {
                statistics = new Statistics();
                statistics.InteractionStatistics(sLoginName, AppId);
            }
    
        }
       public class Validator
        {
            /// <summary>
            /// 判断是否有权限
            /// </summary>
            /// <param name="sLoginName"></param>
            /// <returns></returns>
            public bool Validate(string sLoginName)
            {
                //在数据库中验证用户是否是合法
                if (sLoginName.Equals("铁锅盖"))
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }
    public class Statistics
        {
    
            /// <summary>
            /// 交互统计
            /// </summary>
            /// <param name="sLoginName"></param>
            /// <param name="AppId"></param>
            public string InteractionStatistics(string sLoginName, string AppId)
            {
                return "不同账号和不同游戏服务交互统计信息";
            }
        }
    
    Client:
            /// <summary>
            /// 代理模式
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void btn_proxy_Click(object sender, EventArgs e)
            {
    
                Interaction interaction = new ProxyInteraction();
                txt_description.Text = interaction.InteractionWithGame("铁锅盖", "1001");
            }

总结

优缺点:

  1. 客户通过代理调用功能,降低了耦合性。
  2. 更改代理,可以搭配出许多功能,增加了代码灵活性和可扩展性。
  3. 远程代理可以将功能分散到其他服务端,提高系统效率;缓存代理可使高频率访问对象访问速率更快;保护代理可以控制访问权限。
  4. 在使用者和被使用者中间加了代理,在一定程度上降低了处理速率。

使用场景:

  1. 当客户端对象需要访问远程主机中的对象时可以使用远程代理。
  2. 当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系统开销、缩短运行时间时可以使用虚拟代理,例如一个对象需要很长时间才能完成加载时。
  3. 当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时可以使用缓冲代理。
  4. 当需要控制对一个对象的访问,为不同用户提供不同级别的访问权限时可以使用保护代理。
  5. 当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理。

装饰模式与代理模式区别

从UML类图可以看出来,装饰模式和代理模式在结构上很相似。都是真实类与代理/装饰继承同一个基类。那它们又有何差别啦?

使用代理模式,代理和真实对象之间的的关系通常在编译时就已经确定了,而装饰者能够在运行时动态的被构造。代理模式中,代理类对被代理的对象有控制权,决定其执行或者不执行。而装饰模式中,装饰类对代理对象没有控制权,只能为其增加一层装饰,以加强被装饰对象的功能。

时间: 2024-08-29 02:11:44

对设计模式的总结之装饰模式与代理模式的相关文章

跟我学设计模式视频教程——装饰模式,装饰模式VS代理模式

课程视频 装饰模式 装饰模式VS代理模式1 装饰模式VS代理模式2 课程笔记 课程笔记 课程代码 课程代码 新课程火热报名中 课程介绍 跟我学设计模式视频教程--装饰模式,装饰模式VS代理模式,布布扣,bubuko.com

菜鸟版JAVA设计模式—适配器模式,装饰模式,代理模式异同

节前把3个设计模式的学习心得分享了一下,分别是适配器模式,装饰模式,代理模式. 但是似乎越写越有些迷糊,这三种模式都有着一些大同小异的地方,总是有些搞不清. 今天再重新学习下,把学习心得分享给大家一下.这次是结合的我工作中的实际项目. 设计模式,其实是一种解决方法,JAVA作为一种面向对象的语言,对于所有的设计模式在实现上,总是少不了对接口的实现,对抽象类的继承,有时候似乎是为了去实现接口而去写接口,所以在这三种模式的说明代码中,我们都定义了接口这些东西,所以才有了现在的混淆. 先不厌其烦的介绍

装饰模式与代理模式的区别

以下是我的理解: 代码区别: 装饰模式跟代理模式代码的最大的在于他们的构造方法,代理模式的构造方法不传参数,在构造方法内部完成参数传递,装饰模式将装饰的对象作为参数传进去. 理念区别: 代理模式中,代理类对被代理的对象有控制权,决定其执行或者不执行.而装饰模式中,装饰类对代理对象没有控制权,只能为其增加一层装饰,以加强被装饰对象的功能   学习AOP时,教材上面都说使用的是动态代理,可是在印象中代理模式一直都是控制访问什么的,怎么又动态增加行为了,动态增加行为不是装饰器模式吗?于是 找了很多资料

设计模式(十三): Proxy代理模式 -- 结构型模式

  设计模式(十一)代理模式Proxy(结构型) 1.概述 因为某个对象消耗太多资源,而且你的代码并不是每个逻辑路径都需要此对象, 你曾有过延迟创建对象的想法吗 ( if和else就是不同的两条逻辑路径) ? 你有想过限制访问某个对象,也就是说,提供一组方法给普通用户,特别方法给管理员用户?以上两种需求都非常类似,并且都需要解决一个更大的问题:你如何提供一致的接口给某个对象让它可以改变其内部功能,或者是从来不存在的功能? 可以通过引入一个新的对象,来实现对真实对象的操作或者将新的对象作为真实对象

装饰模式与代理模式的区别(转载)

在整理设计模式时发现代理模式和装饰器模式非常相似,所以找了篇文章来更好的理解下装饰器和代理模式的区别 学习AOP时,教材上面都说使用的是动态代理,可是在印象中代理模式一直都是控制访问什么的,怎么又动态增加行为了,动态增加行为不是装饰器模式吗?于是 找了很多资料,想弄清楚这两者之间到底有什么区别.结果发现这一篇英文文章讲的很清晰,就翻译一下,供参考. 首先,让我们先看一下下面的这两个UML类图,他们分别描述了装饰器模式和代理模式的基本实现. 这两个图可能使我们产生困惑.这两个设计模式看起来很像.对

java/android 设计模式学习笔记(9)---代理模式

这篇博客我们来介绍一下代理模式(Proxy Pattern),代理模式也成为委托模式,是一个非常重要的设计模式,不少设计模式也都会有代理模式的影子.代理在我们日常生活中也很常见,比如上网时连接的代理服务器地址,更比如我们平时租房子,将找房子的过程代理给中介等等,都是代理模式在日常生活中的使用例子. 代理模式中的代理对象能够连接任何事物:一个网络连接,一个占用很多内存的大对象,一个文件,或者是一些复制起来代价很高甚至根本不可能复制的一些资源.总之,代理是一个由客户端调用去访问幕后真正服务的包装对象

装饰模式和代理模式的区别

转自:http://www.cnblogs.com/jaredlam/archive/2011/11/08/2241089.html 学习AOP时,教材上面都说使用的是动态代理,可是在印象中代理模式一直都是控制访问什么的,怎么又动态增加行为了,动态增加行为不是装饰器模式吗?于是 找了很多资料,想弄清楚这两者之间到底有什么区别.结果发现这一篇英文文章讲的很清晰,就翻译一下,供参考. 首先,让我们先看一下下面的这两个UML类图,他们分别描述了装饰器模式和代理模式的基本实现. 这两个图可能使我们产生困

设计模式学习笔记(一)--代理模式

近日在研究设计模式,感觉代理模式是要重点掌握的,在这里记录一下. 什么是代理模式?比较官方的解释是:为其他对象设置一种代理以控制对实际对象的访问. 为了更好的理解代理模式,这里举个例子.在驻外现场,有一个项目经理."二把手"和若干项目组成员,项目经理每天要安排项目组成员任务,主持晨会,写报告汇报工作进展.可是有几个月项目经理要回总部,这里就需要"二把手"来安排项目组成员任务,主持晨会,写报告汇报工作进展.而在这期间新来的同事只知"二把手",而不识

适配器模式、装饰模式、代理模式异同

此三者均是对旧的对象进行封装以提供新的行为,实际上都包含了代理的概念,但实际使用时又有所区别: 适配器模式:将A接口的实现封装后表现出了B接口的行为 装饰模式:将A接口的实现封装后仍然表现出A接口的行为,但在行为逻辑上与原来不同 代理模式:将A接口的实现封装后仍然表现出A接口的行为,这一点上与装饰模式相同,所以非常容易混淆;实际上装饰模式确实可以认为是代理模式的一种类型,因为装饰模式的代理目标局限于一个普通的Java对象,而代理模式中代理目标则更自由,远程代理中它是存在于另一台主机上,虚拟代理中