拜读经典——大话设计模式(四)——装饰模式、代理模式、工厂方法模式

前言

最近一直在忙花里胡哨的东西,忘了巩固代码内功,今天来补上。

顺便一提,今天过了科目三了,开心

 

 

 

 


装饰模式

引言

这个设计模式可是厉害了,我们使用的QQ、网络游戏都有那种换装系统,不同衣服、裤子、鞋有各种各样的组合,如果开发一个程序,这样的组合如果没有巧妙的设计模式肯定会带来庞大的耦合性,用简单工厂模式的话也不能应对如此灵活且不稳定的构造情况,这就需要我们使用装饰模式

装饰模式

装饰模式(Decorator):动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。

Component是定义一个对象接口,可以给这些对象动态地添加职责。

ConcreteComponent是定义一个具体的对象,也可以给这个对象添加一些职责(学英语:concrete-有形的、混凝土的)。

Decorator,装饰抽象类,继承了Component,从外类来扩展Component类的功能,但对于Component来说,是无需知道Decorator的存在的(正如依赖倒转原则的精髓:细节依赖于抽象、抽象不依赖于细节)。

至于ConcreteDecorator就是具体的装饰对象,起到给Component添加职责的功能。

 

基本框架

我们来看看基本的代码实现

    abstract class Component
    {
        //Operation:手术、行为、活动
        public abstract void Operation();
    }
    class ConcreteComponent : Component
    {
        public override void Operation()
        {
            Console.WriteLine("具体对象的具体操作");
        }
    }
    abstract class Decorator : Component
    {
        protected Component component;
        //设置Component
        public void SetComponent(Component component) {
            this.component = component;
        }
        //重写Operation(),实际执行的是Component的Operation()
        public override void Operation()
        {
            if (component != null) {
                component.Operation();
            }
        }
    }
    class ConcreteDecoratorA : Decorator
    {
        //本类独有的方法,以区别于ConcreteDecoratorB
        private string addedState;
        public override void Operation()
        {
            /*
             * 首先运行原Component的Operation(),再执行
             * 本类的功能,如addedState,相当于对原Component
             * 进行了装饰
             */
            base.Operation();
            addedState = "New State";
            Console.WriteLine("具体装饰对象A的操作");
        }
    }
    class ConcreteDecoratorB : Decorator
    {
        public override void Operation()
        {
            /*
             * 首先运行原Component的Operation(),再执行
             * 本类的功能,如AddedBehavior,相当于对原
             * Component进行了装饰
             */
            base.Operation();
            AddedBehavior();
            Console.WriteLine("具体装饰对象B的操作");
        }
        //本类独有的方法,以区别于ConcreteDecoratorA
        private void AddedBehavior() {

        }
    }

主函数:

    static class Program
    {
        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [STAThread]
        static void Main()
        {
            ConcreteComponent c = new ConcreteComponent();
            ConcreteDecoratorA d1 = new ConcreteDecoratorA();
            ConcreteDecoratorB d2 = new ConcreteDecoratorB();

            /* 装饰的方法是:首先用ConcreteConponent实例
             * 化对象c,然后用ConcreteDecoratorA的实例化
             * 对象d1来包装c,再用ConcreteDecoratorB的对
             * 象d2来包装d1,最终执行d2的Operation()
             */
            d1.SetComponent(c);
            d2.SetComponent(d1);
            d2.Operation();

            Console.Read();
        }
    }

装饰模式使用SetComponent来对对象进行包装的。

每个装饰对象的实现和如何使用对象分离开了,每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链当中。

我们对对象的处理不再像简单工厂模式那样对象间是一个平行的关系,而是一个层层包装的关系,我很怀疑python的装饰器语法底层就是这个思路。

另外,根据情况,可以不必设置Component老父亲这个类,可以直接让Decorator这个类继承自ConcreteDecorator这个类,同样道理,如果只有一个ConcreteDecorator这个类,就可以直接把Decorator和ConcreteDecorator的责任合并成一个类。

实践应用

比如说人物的换装问题:

namespace 控制台的装饰模式
{
    class Person   //相当于ConcreteComponent类
    {
        private string name;

        public Person()
        {
        }
        public Person(string name)
        {
            this.name = name;
        }
        public virtual void Show()
        {
            Console.WriteLine("装扮的{0}", name);
        }
    }
    abstract class Finery : Person //相当于Decorator类
    {
        protected Person component;
        public void Decorator(Person component)
        {
            this.component = component;
        }
        public override void Show()
        {
            if (component != null)
            {
                component.Show();
            }
        }
    }
    class TShirts : Finery  //相当于ConcreteDecoratorA类
    {
        public override void Show()
        {
            Console.WriteLine("大T恤 ");
            base.Show();
        }
    }
    class BigTrouser : Finery  //相当于ConcreteDecoratorB类
    {
        public override void Show()
        {
            Console.WriteLine("垮裤 ");
            base.Show();
        }
    }
}

主函数:

    class Program
    {
        static void Main(string[] args)
        {
            Person ox = new Person("小菜");
            Console.WriteLine("\n第一种装扮:");

            BigTrouser kk = new BigTrouser();
            TShirts dtx = new TShirts();

            //开始装扮
            kk.Decorator(ox);
            dtx.Decorator(kk);
            dtx.Show();

            Console.Read();
        }
    }

我这里例子有点少,没有例举其他的穿衣情况,主要开了个头吧,各位朋友可以自行升级。

基于这种设计模式,我很喜欢,尚未实践,但是有此想法:如果用Unity给人物换装,我想就是利用switch得到玩家选出的三个对象——衣服、裤子、鞋,然后传给一个方法,方法里进行这种设计模式的装饰即可。

 

装饰模式总结

装饰模式是为已有功能动态添加更多功能的一种方式。当系统需要新功能时,是向旧的类中添加新的代码。这些新加的代码通常装饰了原有类的核心职责或主要行为。

总结一下就是:装饰模式有效的把类的核心职责和装饰功能分开了,而且可以去除相关类中重复的装饰逻辑,当需要执行特殊行为时,客户代码就可以在运行时根据需要有选择地、按顺序地使用装饰模式功能包装对象了

 

 

 


代理模式

代理模式概念及框架

代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问。

程序框架:

namespace ProxyModule
{
    /* Subject类,定义了RealSubmit和Proxy的公用接口,
     * 这样就在任何使用RealSubject的地方都可以使用Proxy
     * */
    abstract class Subject
    {
        public abstract void Request();
    }
    //RealSubject类:定义Proxy所代表的真实实体
    class RealSubject : Subject
    {
        public override void Request()
        {
            Console.WriteLine("真实的请求");
        }
    }
    /* Proxy类,保存一个引用使得代理可以访问实体,
     * 并提供一个与Subject的接口相同的接口,
     * 这样代理就可以用来代替实体。
     * */
    class Proxy : Subject
    {
        RealSubject realSubject;
        public override void Request()
        {
            if (realSubject == null) {
                realSubject = new RealSubject();
            }
            realSubject.Request();
        }
    }
}

主函数:

        static void Main(string[] args)
        {
            Proxy proxy = new Proxy();
            proxy.Request();

            Console.Read();
        }

输出:真实的请求

 

这个代码相信不难看懂,不过你一定好奇了,这代理感觉好像没有实际的开发上的好处呀,别急,我们这就介绍代理模式的应用。

 

代理模式的应用

  • 远程代理:也就是为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实。
  • 虚拟代理:是根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。
    • 这样可以达到性能的最优化。比如说你打开一个非常大的HTML网页,里面可能有很多的文字和图片,但你还是可以很快打开它,此时你所看到的是所有的文字,但图片却是一张一张地下载后才能看到。那些未打开的图片框,就是通过虚拟代理来替代了真实的图片,此时代理存储了真实图片的路径和尺寸。
  • 安全代理:用来控制真实对象访问时的权限。
  • 智能指引:是指当调用真实的对象时,代理处理另外一些事
    • 如计算真实对象的引用次数,这样当该对象没有引用时,就可以自动释放它;或当第一次引用一个持久对象时,将它装入内存;或在访问一个实际对象前,检查是否已经锁定它,以确保其他对象不能改变它。他们都是通过代理在访问一个对象时附加一些业务内处理。

 


工厂方法模式

复习简单工厂模式

我们第一个学习的设计模式就是“简单工厂模式”,简单工厂模式讲究的是通过一个总的工厂对象来根据传递的参数返回一个实例化操作对象,根据多态来调用不同对象的重写方法来求得不同的值。

namespace 工厂方法
{
    class OperationFactory
    {
        public static Operation createOperate(string operate)
        {
            Operation oper = null;
            switch (operate)
            {
                case "+":
                    oper = new OperationAdd();
                    break;
                case "-":
                    oper = new OperationSub();
                    break;
                case "*":
                    oper = new OperationMul();
                    break;
                case "/":
                    oper = new OperationDiv();
                    break;
            }
            return oper;
        }
    }

    class Operation
    {
        private double _numberA = 0;  
        private double _numberB = 0;  

        public double NumberA    //属性
        {
            get { return _numberA; }
            set { _numberA = value; }
        }
        public double NumberB
        {
            get { return _numberB; }
            set { _numberB = value; }
        }
        public virtual double GetResult()
        {
            double result = 0;
            return result;
        }
    }
    class OperationAdd : Operation
    {
        public override double GetResult()
        {
            double result = 0;
            result = NumberA + NumberB;
            return result;
        }
    }
    class OperationSub : Operation
    {
        public override double GetResult()
        {
            double result = 0;
            result = NumberA - NumberB;
            return result;
        }
    }
    class OperationMul : Operation
    {
        public override double GetResult()
        {
            double result = 0;
            result = NumberA * NumberB;
            return result;
        }
    }
    class OperationDiv : Operation
    {
        public override double GetResult()
        {
            double result = 0;
            if (NumberB == 0)
            {
                throw new Exception("除数不能为0");
            }
            result = NumberA / NumberB;
            return result;
        }
    }
}
    class Program
    {
        static void Main(string[] args)
        {
            Operation oper;
            oper = OperationFactory.createOperate("+");
            oper.NumberA = 1;
            oper.NumberB = 2;
            Console.WriteLine(oper.GetResult());

            Console.ReadLine();
        }
    }

利用简单工厂模式,使得程序非常容易扩展,耦合性很低。

 

工厂方法模式

简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。例如上面的计算器,只要给工厂一个“+”,工厂就返回你一个加法运算对象,然后利用改对象即可得到加法运算结果,如果要对简单工厂模式添加新功能,我们就需要给工厂添加新的case分支,修改原有的类,我们不但对扩展开放了,对修改也开放了,这不符合开放-封闭原则。

所以我们有了工厂方法模式:

工厂方法模式(Factory Method):定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

工厂方法模式相较于简单工厂模式最显著的变化就是更加符合了开放-封闭原则的精神,使整个程序都没有修改的变化,而是只有扩展的变化。

工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在的,也就是说,工厂方法把简单工厂的内部逻辑判断转移到了客户端的代码中进行,添加新功能就不得不修改客户端。

namespace 工厂方法
{
    //总工厂
    interface IFactory {
        Operation CreateOperation();
    }

    //加法工厂
    class AddFactory : IFactory
    {
        public Operation CreateOperation()
        {
            return new OperationAdd();
        }
    }
    //减法工厂
    class SubFactory : IFactory
    {
        public Operation CreateOperation()
        {
            return new OperationSub();
        }
    }
     
    //产品Product父类
    class Operation
    {
        private double _numberA = 0;
        private double _numberB = 0;

        public double NumberA
        {
            get { return _numberA; }
            set { _numberA = value; }
        }
        public double NumberB
        {
            get { return _numberB; }
            set { _numberB = value; }
        }
        public virtual double GetResult()
        {
            double result = 0;
            return result;
        }
    }
    class OperationAdd : Operation
    {
        public override double GetResult()
        {
            double result = 0;
            result = NumberA + NumberB;
            return result;
        }
    }
    class OperationSub : Operation
    {
        public override double GetResult()
        {
            double result = 0;
            result = NumberA - NumberB;
            return result;
        }
    }
}

主类:

    class Program
    {
        static void Main(string[] args)
        {
            IFactory factory = new SubFactory();  //这里可以修改成不同工厂
            Operation oper=factory.CreateOperation();
            
            oper.NumberA = 1;
            oper.NumberB = 2;
            Console.WriteLine(oper.GetResult());

            Console.ReadLine();
        }
    }

如果要把加法换成减法,只需要修改客户端的那一处就好了,尽管我们前面说尽量不要动客户端,但是这里也是万不得已。

如果新加算法,就不需要再动工厂类了,添加新的ConcreteProduct(产品)、添加对应ConcreteCreator(工厂),然后改一下客户端的工厂,然后生产对象即可。

简单工厂模式和工厂方法模式都集中封装了对象的创建,使得要更换对象时,不需要做大的改动就可以实现。降低了客户程序与产品对象的耦合。工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,且客服了缺点,但是缺点是由于每增加一个产品,就需要重新加产品工厂的类,增加了额外的开销量。

至于客户端的修改,这个麻烦的事情还是等“反射”登场再考虑吧。

 

 

 

 


 

 

商业转载 请联系作者获得授权,非商业转载 请标明出处,谢谢

发表评论