大话设计模式(四) 组合模式 迭代模式 单列模式 桥接模式 命令模式 职责链模式

分公司=一部门——组合模式

组合模式(Composite),将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对耽搁对象和组合对象的使用具有一致性。

image

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Component
{
    class Program
    {
        static void Main(string[] args)
        {
            Composite root = new Composite("root");
            root.Add(new Leaf("Leaf A"));
            root.Add(new Leaf("Leaf B"));

            Composite comp = new Composite("Composite X");
            comp.Add(new Leaf("Leaf XA"));
            comp.Add(new Leaf("Leaf XB"));

            root.Add(comp);

            Composite comp2 = new Composite("Composite XY");
            comp2.Add(new Leaf("Leaf XYA"));
            comp2.Add(new Leaf("Leaf XYB"));

            comp.Add(comp2);

            root.Add(new Leaf("Leaf C"));

            Leaf leaf = new Leaf("Leaf D");
            root.Add(leaf);
            root.Remove(leaf);

            root.Display(1);
            Console.Read();
        }
    }
    abstract class Component
    {
        protected string name;
        public Component(string name)
        {
            this.name = name;
        }
        //通常都用Add和Remove方法来提供增加或益处树叶或树枝的功能
        public abstract void Add(Component c);
        public abstract void Remove(Component c);
        public abstract void Display(int depth);
    }

    class Leaf : Component
    {
        public Leaf(string name) : base(name) { }

        /*由于叶子没有再增加分枝和树叶,所以Add和Remove方法实现它没有意义,
         *但是这样做可以消除叶子节点和枝节点对象在抽象层次的区别,他们具有完全
         *一致的接口
         */

        public override void Add(Component c)
        {
            Console.WriteLine("Cannot add a leaf");
        }

        public override void Remove(Component c)
        {
            Console.WriteLine("Cannot remove from a leaf");
        }
        //叶节点的具体方法,此处是显示其名称和级别
        public override void Display(int depth)
        {
            Console.WriteLine(new String('-', depth) + name);
        }
    }
    //Composite定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关的操作
    class Composite : Component
    {
        private List<Component> childrean = new List<Component>();

        public Composite(string name) : base(name) { }

        public override void Add(Component c)
        {
            childrean.Add(c);
        }

        public override void Remove(Component c)
        {
            childrean.Remove(c);
        }

        public override void Display(int depth)
        {
            Console.WriteLine(new String('-', depth) + name);

            foreach (Component component in childrean)
            {
                component.Display(depth + 2);
            }
        }
    }
}

运行结果如下:

image

透明方式与安全方式

透明方式:在Component中声明所有用来管理子对象的方法,其中包括Add,Remove等,实现Component接口的所有子类都具备了Add和Remove。这样做的好处就是叶子节点和枝节点对于外界没有区别,他们具有完全按一致的行为接口。但问题也很明显,因为Leaf类本身不具备Add(),Remove()方法的功能,所以实现它没有意义的。

安全方式:也就是在Component接口中部声明Add和Remove方法,那么子类的Leaf也就不需要去实现它,而在Composite声明所有用来管理子类对象的方法,不过由于不透明,所以树叶和树枝类将不具有相同的接口,客户端的调用需要做相应的判断,带来了不便s'd'f

使用场景:需求中是体现部分与整体层次的结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑用组合模式了。

公司管理系统:

namespace Component_Company
{
    abstract class Company
    {
        protected string name;

        public Company(string name)
        {
            this.name = name;
        }

        public abstract void Add(Company c);//增加
        public abstract void Remove(Company c);//移除
        public abstract void Display(int depth);//显示
        public abstract void LineOfDuty();//履行职责

        //具体公司类 实现接口 树枝节点
        class ConcreteCompany : Company
        {
            private List<Company> children = new List<Company>();

            public ConcreteCompany(string name)
                : base(name)
            { }



            public override void Add(Company c)
            {
                children.Add(c);
            }

            public override void Remove(Company c)
            {
                children.Remove(c);
            }

            public override void Display(int depth)
            {
                Console.WriteLine(new String('-', depth) + name);

                foreach (Company component in children)
                {
                    component.Display(depth + 2);
                }
            }

            public override void LineOfDuty()
            {
                foreach (Company component in children)
                {
                    component.LineOfDuty();
                }
            }
        }

        //人力资源部与财务部类 树叶节点
        class HRDepartment : Company
        {
            public HRDepartment(string name) : base(name) { }



            public override void Add(Company c)
            {

            }

            public override void Remove(Company c)
            {

            }

            public override void Display(int depth)
            {
                Console.WriteLine(new String('-', depth) + name);
            }

            public override void LineOfDuty()
            {
                Console.WriteLine("{0}员工招聘培训管理", name);
            }
        }

        //财务部
        class FinanceDepartment : Company
        {
            public FinanceDepartment(string name) : base(name) { }
            public override void Add(Company c)
            {

            }

            public override void Remove(Company c)
            {

            }

            public override void Display(int depth)
            {
                Console.WriteLine(new String('-', depth) + name);
            }

            public override void LineOfDuty()
            {
                Console.WriteLine("{0}公司财务收支管理", name);
            }
        }

        public class Run
        {
            public static void Go()
            {
                ConcreteCompany root = new ConcreteCompany("北京总公司");
                root.Add(new HRDepartment("总公司人力资源部"));
                root.Add(new FinanceDepartment("总公司财务部"));

                ConcreteCompany comp = new ConcreteCompany("上海华东分公司");
                comp.Add(new HRDepartment("上海华东分公司人力资源部"));
                comp.Add(new FinanceDepartment("上海华东分公司财务部"));
                root.Add(comp);

                ConcreteCompany comp1 = new ConcreteCompany("南京办事处");
                comp1.Add(new HRDepartment("南京办事处人力资源部"));
                comp1.Add(new FinanceDepartment("南京办事处司财务部"));
                comp.Add(comp1);

                ConcreteCompany comp2 = new ConcreteCompany("杭州办事处");
                comp2.Add(new HRDepartment("杭州办事处人力资源部"));
                comp2.Add(new FinanceDepartment("杭州办事处司财务部"));
                comp.Add(comp2);

                Console.WriteLine("
结构图");
                root.Display(1);

                Console.WriteLine("
职责");
                root.LineOfDuty();

                Console.Read();
            }

        }
    }
}

image

想走?可以!先买票——迭代器模式

迭代器模式(Iterator):提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示

image

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Iterator
{
    class Program
    {
        static void Main(string[] args)
        {
            ConcreteAggregate a = new ConcreteAggregate();
            a[0] = "aa";
            a[1] = "bb";
            a[2] = "cc";
            a[3] = "dd";
            a[4] = "ee";
            a[5] = "ff";

            Iterator i = new ConcreteIterator(a);
            object item = i.First();
            while (!i.IsDone())
            {
                Console.WriteLine("{0}请买车票", i.CurrentItem());
                i.Next();
            }
            Console.Read();
        }
    }
    //迭代器抽象类
    abstract class Iterator
    {
        /*用于得到开始对象,得到下一个对象判断是否到结尾
         *当前对象等抽象方法,统一接口
         */
        public abstract object First();
        public abstract object Next();
        public abstract bool IsDone();
        public abstract object CurrentItem();
    }

    abstract class Aggregate
    {
        //创建迭代器
        public abstract Iterator CreateIterator();
    }

    //具体迭代器类
    class ConcreteIterator : Iterator
    {
        //定义了一个具体聚集对象
        private ConcreteAggregate aggregate;
        private int current = 0;

        //初始化时将具体的聚集对象传入
        public ConcreteIterator(ConcreteAggregate aggregate)
        {
            this.aggregate = aggregate;
        }
        public override object First()
        {
            return aggregate[0];
        }

        public override object Next()
        {
            object ret = null;
            current++;
            if (current < aggregate.Count)
            {
                ret = aggregate[current];
            }
            return ret;
        }

        public override bool IsDone()
        {
            return current >= aggregate.Count ? true : false;
        }

        public override object CurrentItem()
        {
            return aggregate[current];
        }
    }
    //具体聚集类 继承Aggregate
    class ConcreteAggregate : Aggregate
    {
        private IList<object> items = new List<object>();



        public override Iterator CreateIterator()
        {
            return new ConcreteIterator(this);
        }

        public int Count
        {
            get { return items.Count; }
        }

        public object this[int index]
        {
            get { return items[index]; }
            set { items.Insert(index, value); }
        }
    }
}

运行结果:image

迭代器模式就是分离了集合对对象的遍历行为,抽象出一个迭代类来负责,这样可以做到不暴露集合内部结构,又可让外部代码透明地访问集合内部的数据(自我感觉以上部分有点难懂,至少相对于以前来说。)

有些类也需要计划生育——单列模式

单例模式(Singleton):保证一个类仅有一个实例,并体统一个访问它的全局访问点。

通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保护她它的唯一实例。这个类可以保证没有其他实例可以被创建

下面看一个代码就很容易理解这个模式了:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Singleton
{
    class Program
    {
        static void Main(string[] args)
        {
            Singleton s1 = Singleton.GetInstance();
            Singleton s2 = Singleton.GetInstance();

            if (s1 == s2)
            {
                Console.WriteLine("两个对象是相同的实例");
            }
            Console.Read();
        }
    }
    class Singleton
    {
        private static Singleton instance;

        //构造放让其private,这就杜丝勒外界利用new 创建此类的实力的可能
        private Singleton()
        {

        }

        //此方法是获得本类实例的唯一全局访问点
        public static Singleton GetInstance()
        {
            if (instance == null)
            {
                instance = new Singleton();
            }
            return instance;
        }
    }
}

多线程时的单例

多线程的时候会出现多个线程同时调用GetInstance()方法,会造成创建实例。这时候我们就要用到锁

lock是确保当一个线程位于代码临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则将他一直等待(即被阻止),知道该对象被释放。

class Singleton_syn
    {
        private static Singleton_syn instance;
        //程序运行时创建一个静态只读的进程辅助对象
        private static readonly object synRoot = new object();

        //构造放让其private,这就杜丝勒外界利用new 创建此类的实力的可能
        private Singleton_syn()
        {

        }

        //在同一个时刻加了锁的那部分程序只有一个线程可以进入
        public static Singleton_syn GetInstance()
        {
            lock (synRoot)
            {
                if (instance == null)
                {
                    instance = new Singleton_syn();
                }
            }
            return instance;
        }

这样,每次调用GetInstance方法时,都需要lock,我们改良:

class Singleton_syn
    {
        private static Singleton_syn instance;
        //程序运行时创建一个静态只读的进程辅助对象
        private static readonly object synRoot = new object();

        //构造放让其private,这就杜丝勒外界利用new 创建此类的实力的可能
        private Singleton_syn()
        {

        }

        //在同一个时刻加了锁的那部分程序只有一个线程可以进入
        public static Singleton_syn GetInstance()
        {
            if (instance == null)
            {
                lock (synRoot)
                {
                    if (instance == null)
                    {
                        instance = new Singleton_syn();
                    }
                }
            }
            return instance;
        }
    }

静态初始化

C#与公共语言运行库也提供了一种‘静态初始化’方法,这种方法不需要开发人员显式地编写线程安全代码,即可解决多线程环境下他是不安全的问题。

//sealed 阻止发生派生类,而派生类可能会增加实例
    public sealed class Singleton_sealed
    {
        //在第一次引用类的任何成员时创建实例,公共语言运行时负责处理变量的初始化
        private static readonly Singleton_sealed instance = new Singleton_sealed();
        private Singleton_sealed() { }
        public static Singleton_sealed GetInstance()
        {
            return instance;
        }
    }

手机软件何系统——桥接模式

继承带来的麻烦:对象的继承关系是在编译时就定义好了,所以无法在运行时改变从弗雷继承的实现。子类的实现与他的父类有非常紧密的依赖关系,以至于弗雷实现中的任何变化必然会导致子类发生变化,当你需要复用子类使,如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换,这种依赖关系限制了灵活性并最终限制了复用性

合成/聚合复用原则:尽量使用合成/聚合,尽量不要使用类继承

优先使用对象合成/聚合有助于你保持每个类被封装,并被集中在单个任务上,这样类和类继承层次就会保持最小规模,并且不太可能增长为不可控制的庞然大物

松耦合程序

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CARP
{
    class Program
    {
        static void Main(string[] args)
        {
            HandsetBrand ab;
            ab = new HandsetBrandN();

            ab.SetHandsetSoft(new HandsetGame());
            ab.Run();

            ab.SetHandsetSoft(new HandsetAddressList());
            ab.Run();

            ab = new HandsetBrandM();

            ab.SetHandsetSoft(new HandsetGame());
            ab.Run();

            ab.SetHandsetSoft(new HandsetAddressList());
            ab.Run();

            Console.Read();
        }
    }

    abstract class HandsetSoft
    {
        public abstract void Run();
    }

    //手机游戏
    class HandsetGame : HandsetSoft
    {

        public override void Run()
        {
            Console.WriteLine("运行手机游戏");
        }
    }

    //手机通讯录
    class HandsetAddressList : HandsetSoft
    {

        public override void Run()
        {
            Console.WriteLine("运行手机通讯录");
        }
    }

    abstract class HandsetBrand
    {
        protected HandsetSoft soft;

        //设置手机软件

        public void SetHandsetSoft(HandsetSoft soft)
        {
            this.soft = soft;
        }
        //运行
        public abstract void Run();
    }

    class HandsetBrandN : HandsetBrand
    {

        public override void Run()
        {
            soft.Run();
        }
    }

    class HandsetBrandM : HandsetBrand
    {

        public override void Run()
        {
            soft.Run();
        }
    }
}

桥接模式(Bridge)

定义:将抽象部分与它的实现部分分离,使它们都可以独立地变化。

什么叫抽象与它的实现分离,这并不是说,让抽象类与其派生类分离,因为这没有任何意义。实现指的是抽象类和他的派生类用来实现自己的对象。

桥接模式代码

image

根据这个结构图,我们实现了如下的代码,其实我觉得这个代码确实值得仔细研究研究,其实要是没有设计器,我还不一定能猜出这结果呢。。。不过看了第二遍就简单了一些。

namespace CARP_Demo
{
    abstract class Implementor
    {
        public abstract void Operation();
    }

    class ConcreteImplementorA : Implementor
    {
        public override void Operation()
        {
            Console.WriteLine("具体实现A的方法执行");
        }
    }
    class ConcreteImplementorB : Implementor
    {
        public override void Operation()
        {
            Console.WriteLine("具体实现B的方法执行");
        }
    }

    class Abstraction
    {
        protected Implementor implementor;

        public void SetImplementor(Implementor implementor)
        {
            this.implementor = implementor;
        }
        public virtual void Operation()
        {
            implementor.Operation();
        }
    }

    class RefineAbstraction : Abstraction
    {
        public override void Operation()
        {
            implementor.Operation();
        }
    }

    public class Run
    {
        public static void Go()
        {
            Abstraction ab = new RefineAbstraction();

            ab.SetImplementor(new ConcreteImplementorA());
            ab.Operation();

            ab.SetImplementor(new ConcreteImplementorB());
            ab.Operation();
        }

    }
}

烤羊肉串引来的思考——命令模式

让编程融入生活。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Command
{
    class Program
    {
        static void Main(string[] args)
        {
            //开店前准备
            Barbecuer boy = new Barbecuer();
            Command bakeMuttonCommand1 = new BakeMuttonCommand(boy);
            Command bakeMuttonCommand2 = new BakeMuttonCommand(boy);
            Command bakeChickenWingCommand1 = new BakeChickenCommand(boy);

            Waiter girl = new Waiter();
            //开门营业,顾客点菜
            girl.SetOrder(bakeMuttonCommand1);
            girl.SetOrder(bakeMuttonCommand2);
            girl.SetOrder(bakeChickenWingCommand1);

            //点完菜通知厨房
            girl.Notify();

            Console.Read();
        }
    }

    public class Barbecuer
    {
        public void BakeMutton()
        {
            Console.WriteLine("烤羊肉串!");
        }

        public void BakeChickenWing()
        {
            Console.WriteLine("烤鸡翅!");
        }
    }


    public abstract class Command
    {
        protected Barbecuer receiver;

        public Command(Barbecuer receiver)
        {
            this.receiver = receiver;
        }

        //执行命令
        abstract public void ExcuteCommand();
    }

    class BakeMuttonCommand : Command
    {
        public BakeMuttonCommand(Barbecuer receiver) : base(receiver) { }
        public override void ExcuteCommand()
        {
            receiver.BakeMutton();
        }
    }

    class BakeChickenCommand : Command
    {
        public BakeChickenCommand(Barbecuer receiver) : base(receiver) { }
        public override void ExcuteCommand()
        {
            receiver.BakeChickenWing();
        }
    }
    public class Waiter
    {
        private IList<Command> orders = new List<Command>();

        //设置订单
        public void SetOrder(Command command)
        {
            if (command.ToString() == "Command.BakeChickenCommand")
            {
                Console.WriteLine("服务员:鸡翅没有了,请点别的烧烤");
            }
            else
            {
                orders.Add(command);
                Console.WriteLine("增加订单:" + command.ToString() + " 时间:" + DateTime.Now.ToString());
            }
        }

        //取消订单
        public void CancelOrder(Command command)
        {
            orders.Remove(command);
            Console.WriteLine("取消订单:" + command.ToString() + " 时间:" + DateTime.Now.ToString());
        }

        //通知全部执行
        public void Notify()
        {
            foreach (Command cmd in orders)
            {
                cmd.ExcuteCommand();
            }
        }
    }
}

image

通过上面的例子,我们引入又一个模式:

命令模式(Command):将一个请求封装为一个对象,从而使你可用不同的请求客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

image

根据如上的结构图实现如下的代码:

namespace Command_demo
{
    abstract class Command
    {
        protected Receiver receiver;

        public Command(Receiver receiver)
        {
            this.receiver = receiver;
        }
        abstract public void Execute();
    }

    class ConcreteCommand : Command
    {
        public ConcreteCommand(Receiver receiver) : base(receiver) { }

        public override void Execute()
        {
            receiver.Action();
        }
    }
    //要求该命令执行这个请求
    class Invoker
    {
        private Command command;

        public void SetCommand(Command command)
        {
            this.command = command;
        }

        public void ExecuteCommand()
        {
            command.Execute();
        }
    }
    class Receiver
    {
        public void Action()
        {
            Console.WriteLine("执行请求!");
        }
    }

    public class Run
    {
        public static void Go()
        {
            Receiver r = new Receiver();
            Command c = new ConcreteCommand(r);
            Invoker i = new Invoker();
            i.SetCommand(c);
            i.ExecuteCommand();
            Console.Read();
        }
    }
}

命令模式的有点:

一、它能较容易地设计一个命令队列;

二、在需要的情况下,可以较容易地将命令计入日志;

三、允许接收请求的一方决定是否需要否决请求;

四、可以容易地实现对请求的撤销和重做;

五、由于加进新的具体命令类不影响其他的类,因此增加新的具体命令类很容易。

最重要一点:命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分割开。

敏捷开发原则告诉我们,不要为了代码添加基于猜测的、实际不需要的功能,如果不清楚一个系统是否需要命令模式,一般就不要着急去实现它,事实上,在需要的时候通过重构实现这个模式并不困难,只有在真正需要如撤销/恢复操作等功能时,吧原来的代码重构为命令模式才有意义。

加薪非要老总批?——职责链模式

Chain of Responsibility:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象练成一条链,并沿着这条链传递该请求,知道又一个对象处理它位置。

image

根据这个结构图实现如下代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Chain_of_Responsibility
{
    class Program
    {
        static void Main(string[] args)
        {
            Handler h1 = new ConcreteHandler1();
            Handler h2 = new ConcreteHandler2();
            Handler h3 = new ConcreteHandler3();
            //设置职责链上家与下家
            h1.SetSuccessor(h2);
            h2.SetSuccessor(h3);

            int[] requests = { 2, 4, 14, 33, 13, 2, 34, 23 };
            foreach (int request in requests)
            {
                h1.HandleRequest(request);
            }

            Console.Read();
        }

    }
    abstract class Handler
    {
        protected Handler successor;

        public void SetSuccessor(Handler successor)
        {
            this.successor = successor;
        }
        public abstract void HandleRequest(int request);
    }

    //具体类,可以处理也可以发送给后继
    class ConcreteHandler1 : Handler
    {

        public override void HandleRequest(int request)
        {
            if (request >= 0 && request < 10)
            {
                Console.WriteLine("{0}处理请求{1}", this.GetType().Name, request);
            }
            else if (successor != null)
            {
                //转移到下一位
                successor.HandleRequest(request);
            }
        }
    }

    class ConcreteHandler2 : Handler
    {
        public override void HandleRequest(int request)
        {
            if (request >= 10 && request < 20)
            {
                Console.WriteLine("{0}处理请求{1}", this.GetType().Name, request);
            }
            else if (successor != null)
            {
                successor.HandleRequest(request);
            }
        }
    }

    class ConcreteHandler3 : Handler
    {
        public override void HandleRequest(int request)
        {
            if (request >= 120 && request < 30)
            {
                Console.WriteLine("{0}处理请求{1}", this.GetType().Name, request);
            }
            else if (successor != null)
            {
                successor.HandleRequest(request);
            }
        }
    }
}

这其中最关键是:当客户提交一个请求时,请求是沿链传递直至有一个ConcreteHandler对象负责处理它。

这样,接收者和发送者都没有对方的明确信息。且链中的对象自己也并不知道链的结构。结果是职责链可简化对象的相互连接,他们仅需保持一个指向其后继者的引用,而不需要保持它所有的候选者的引用。

原文地址:https://www.cnblogs.com/sunhan/p/3542330.html