zoukankan      html  css  js  c++  java
  • 设计模式研究

    设计模式研究(一)实例比较TemplateMethod与Strategy

    设计模式研究(二)-Singleton 

    本文要讨论的是代理和适配器模式。

    两种模式理念上的差别

    代理(Proxy)模式给某一个对象提供一个代理,并由代理对象控制对原对象的引用。 

    适配器模式(Adapter)把一个类的接口变换成客户端所期待的另一种接口,从而使原本接口不匹配而无法在一起工作的两个类能够在一起工作。

     

     Proxy的关注点是职能转移,引入代理层代替目标端与调用端进行沟通,而且代理层和目标端具有相同的服务结构(继承同一个接口)。

    Adapter的关注点是接口变换,引入一个符合调用端要求的“转化器”实现目标端与调用端的沟通,而且转化器和目标端的服务结构式是不一样的。 

     实例说明

     对于外出打工或外出求学的游子们,大多都有过年回家买车票的经历。下面用代理模式还原一下独具特色的买车票经历。

     先抽象一个火车票接口

        //火车票
        public interface ITicket
        {
            
    string Buy(int price);//车票不紧张的情况下适用
            string Buy(int price, EnumIdentity identity);//车票紧张或购票需求量大的情况下适用
        }
        
    //购票人身份
        public enum EnumIdentity
        {
            外出务工人员 
    = 0,
            黄牛 
    = 1,
        }

    下面是火车站售票系统

        //火车站售票系统
        public class RailwayStation : ITicket
        {
            
    public string Buy(int price) 
            {
                
    string result = string.Empty;
                result 
    = "票价:" + price;
                
    return result;
            }
            
    public string Buy(int price, EnumIdentity identity)
            {
                
    string result = string.Empty;
                
    if (identity == EnumIdentity.黄牛)
                    result 
    = "票价:" + price;
                
    if (identity == EnumIdentity.外出务工人员)
                    result 
    = "车票已售完";
                
    return result;
            }
        }

    看来“外出务工人员”在车票紧张的情况下从火车站是拿不到车票的,职能上作为火车站售票窗口代理的“黄牛”应用而生了!

        //黄牛售票
        public class Scalper:ITicket
        {
            ITicket Ticket 
    = new RailwayStation();
            
    public string Buy(int price)
            {
                
    string result = string.Empty;
                result 
    = "暂不受理";
                
    return result;
            }
            
    public string Buy(int price, EnumIdentity identity)
            {
                
    string result = "手续费:" + price * 0.2;
                result 
    += Ticket.Buy(price, EnumIdentity.黄牛);
                
    return result;
            }
        }

    下面是买票场景重现:

        //外出务工人员过节回家
        public class ProxyClient
        {
            
    public static void Call()
            {
                
    //票价
                int price = 100;
                
    string result = string.Empty;
                ITicket Ticket1 
    = new RailwayStation();
                result 
    = Ticket1.Buy(price, EnumIdentity.外出务工人员);
                Console.WriteLine(
    "去火车站买票:" + result);

                ITicket Ticket2 
    = new Scalper();
                result 
    = Ticket2.Buy(price, EnumIdentity.外出务工人员);
                Console.WriteLine(
    "和黄牛买票:" + result);

            }
        }

    再说一个我的亲身经历,很久以前,笔记本的键盘出现串键现象,于是想外接一个键盘来用。一天,正好看到京东上一款非常便宜而且评价很不错的键盘在做促销。似乎没有太多考虑就下了订单,键盘送到家以后,想起了漩涡鸣人常说的一个词 “纳尼?”,是PS/2口的!

        //PS/2接口
        public interface IPS2
        {
            
    void PS2Connect();
        }
        
    //PS/2接口的键盘
        public class keyboard:IPS2
        {
            
    public void PS2Connect()
            {
                Console.WriteLine(
    "PS/2接口类型的键盘已经连接到电脑");
            }
        }

    我的笔记本只能用USB口的。

        //USB接口
        public interface IUSB
        {
            
    void USBConnect();
        }

    怎么办?幸好有这种东西:PS/2 to USB converter 。

     

        //引入适配器   PS/2 to USB转化器
        public class Adapter : keyboard, IUSB
        {
            
    public void USBConnect()
            {
                
    this.PS2Connect();
            }
        }

    现在可以享用新键盘了!

    // 一台不支持PS/2接口,但支持USB接口的主机
        public class AdapterClient
        {
            
    public static void Call()
            {
                
    //主机与转化器连接
                IUSB usb = new Adapter();
                usb.USBConnect();
            }
        }

    本篇先讨论单件 Singleton,单件的目标是保证一个类型只有一个实例,那么由谁来保证实例的唯一性呢?可能的方案有:

    a)调用端保证。
    调用端调用一个类时,他是不需要也不会去考虑这个类是否已经被实例化的。而且把这样的监管工作交给调用端是很不负责的做法。
    b)类型内部保证。

    类型内部如何保证?

    将实例创建工作放到类型内部,这样类型就可以将实例创建工作监管起来。类型可以知道内部的实例有没有被创建,甚至可以知道创建实例的工作被执行了多少次。

    所以个人认为理解单件需要分为两步:

    1、 监管工作谁来做?实例的监管工作需要类型自己去做。

    2、 监管工作如何做?类型如何保证实例唯一就是技术实现问题了,可以看到的版本有 线程安全的、双重锁定的、延迟初始化的等。

    下面使用伪代码逐步分析实例化工作放到类型内部的做法。

     调用我,实例我给你

        class Singleton
        {
            Singleton Instance 
    = null;
            
    // 实例化类型 Singleton
            Singleton GetInstance()
            {
                Instance 
    = new Singleton();
                
    return Instance;
            }

        }

    你只管调用,我保证唯一

        class Singleton
        {
            Singleton Instance 
    = null;
            
    //实例化类型 Singleton
            Singleton GetInstance()
            {
                Instance 
    = new Singleton();
                
    return Instance;
            }
            
    // 实例化类型 Singleton,实例化时判断类型有没有被创建过,这样就保证了实例的唯一
            Singleton GetInstance()
            {
                
    if (Instance == null)
                {
                    Instance 
    = new Singleton();
                }
                
    return Instance;
            }

        }

    你们都可以调用,我需要统计调用次数

    class Singleton
        {
            Singleton Instance 
    = null;
            
    public int Count { getset; }
            
    //实例化类型 Singleton
            Singleton GetInstance()
            {
                Instance 
    = new Singleton();
                
    return Instance;
            }
            
    // 实例化类型 Singleton,实例化时判断类型有没有被创建过,这样就保证了实例的唯一
            Singleton GetInstance()
            {
                
    if (Instance == null)
                {
                    Instance 
    = new Singleton();
                }
                
    return Instance;
            }
            
    // 实例化类型 Singleton,并且加入一个计数器,这样能知道实例化工作被执行了多少次
            Singleton GetInstance()
            {
                Count
    ++;
                
    if (Instance == null)
                {
                    Instance 
    = new Singleton();
                }
                
    return Instance;
            }

        }

    想使用实例?请出示合法证件

    class Singleton
        {
            Singleton Instance 
    = null;
            
    public int Count { getset; }
            
    //实例化类型 Singleton
            Singleton GetInstance()
            {
                Instance 
    = new Singleton();
                
    return Instance;
            }
            
    // 实例化类型 Singleton,实例化时判断类型有没有被创建过,这样就保证了实例的唯一
            Singleton GetInstance()
            {
                
    if (Instance == null)
                {
                    Instance 
    = new Singleton();
                }
                
    return Instance;
            }
            
    // 实例化类型 Singleton,并且加入一个计数器,这样能知道实例化工作被执行了多少次
            Singleton GetInstance()
            {
                Count
    ++;
                
    if (Instance == null)
                {
                    Instance 
    = new Singleton();
                }
                
    return Instance;
            }
            
    // 实例化类型 Singleton,并且接收一个合法的授权,这样可以知道每个授权方的调用次数,使用频率
            Singleton GetInstance(string caller)
            {
                
    //Check 调用方合法性验证
                if (Check(caller))
                {
                    CallerCount(caller);
                    
    if (Instance == null)
                    {
                        Instance 
    = new Singleton();
                    }
                    
    return Instance;
                }
                
    else
                    
    return null;
            }
            
    //记录调用方调用次数
            public void CallerCount(string caller)
            {
                
    //caller Count++
            }
            
    public bool Check(string caller)
            {
                
    return true;
            }
        }

     欢迎一起讨论!

     --------------------------补充-------------------------------

    我把几种流行的 Singleton 写法发出来,省的大家再去查资料。

      public sealed class MySingleton
        {
            
    static MySingleton instance = null;
            MySingleton() { }
            
    //简单写法
            public static  MySingleton Istance 
            {
                
    get
                {
                    
    if (instance == null)
                    {
                        instance 
    = new MySingleton();
                    }
                    
    return instance;
                }
            }
            
    //线程安全
            static readonly object obj = new object();
            
    public static MySingleton SafeInstance
            {
                
    get
                {
                    
    lock (obj)
                    {
                        
    if (instance == null)
                            instance 
    = new MySingleton();
                        
    return instance;
                    }
                }
            }
            
    //双重锁定 节约开销
            public static MySingleton LockInstance
            {
                
    get
                {
                    
    if (instance == null)
                    {
                        
    lock (obj)
                        {
                            
    if (instance == null)
                                instance 
    = new MySingleton();
                        }
                    }
                    
    return instance;
                }
            }
            
    //静态初始化
            static MySingleton() { }
            
    static readonly MySingleton staticinstance = new MySingleton();
            
    public static MySingleton StaticInstance
            {
                
    get
                {
                    
    return staticinstance;
                }
            }
            
    //延迟初始化
            public static MySingleton lazyInstance
            {
                
    get
                {
                    
    return Lazy.staticinstance;
                }
            }
            
    class Lazy
            {
                
    internal static readonly MySingleton staticinstance = new MySingleton();
                
    static Lazy() { }
            }
        }
    一、模式简介

    1、模板方法

     

    用意:准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。 

     2、策略

    用意:针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换 。

    二、实例比较

    Template Method模式和Strategy模式都可以分离通用的算法和具体的上下文。

    Template Method模式通过继承解决,Strategy通过委托解决。

    分别用以上两个模式来实现冒泡排序。

    1、 Template Method

     public abstract class BubbleSorter
        {
            
    private int operations = 0;
            
    protected int length = 0;
            
    protected int DoSort()
            {
                operations 
    = 0;
                
    if (length < 1)
                    
    return operations;
                
    for (int nextToLast = length - 1; nextToLast >= 0; nextToLast--)
                {
                    
    for (int index = 0; index < nextToLast; index++)
                    {
                        
    if (OutOfOrder(index))
                            Swap(index);
                        operations
    ++;
                    }
                }
                    
    return operations;
            }
            
    protected abstract void Swap(int index);
            
    protected abstract Boolean OutOfOrder(int index);
        }
        
    public class DoubleBubblerSorter:BubbleSorter  
        {
            
    private double[] array = null;
            
    public int Sort(double[] a)
            {
                array 
    = a;
                length 
    = a.Length;
                
    return DoSort();
            }
            
    protected override  void Swap(int index)
            {
                
    double t = array[index];
                array[index] 
    = array[index + 1];
                array[index 
    + 1= t;
            }
            
    protected override Boolean OutOfOrder(int index)
            {
                
    return (array[index] > array[index + 1]);
            }
            
    public void PrintArray()
            {
                
    foreach (var a in array)
                {
                    Console.WriteLine(a);
                }
            }
        }

    通用算法Swap(交换数据),OutOfOrder(是否该交换)被放置在基类中,通过继承,DoubleBubblerSorter实现了针对Double Array的BubblerSorter

    继承关系是强耦合的,BubbleSorter中包含了冒泡排序的算法DoSort。 DoubleBubblerSorter依赖于BubbleSorter。

    运行一下

    DoubleBubblerSorter bs = new DoubleBubblerSorter();
    bs.Sort(new double[] { 12.2342.13.53.84.51.6 });
    bs.PrintArray();

    2、Strategy

     public  class BubbleSorter
        {
            
    private int operations = 0;
            
    private int length = 0;
            
    private SortHandle sorthandle = null;
            
    public BubbleSorter(SortHandle sh)
            {
                sorthandle = sh;
            }

            
    public int Sort(object array)
            {
                sorthandle.SetArray(array);
                length = sorthandle.Length();
                operations = 0;
                
    if (length < 1)
                    
    return operations;
                
    for (int nextToLast = length - 1; nextToLast >= 0; nextToLast--)
                {
                    
    for (int index = 0; index < nextToLast; index++)
                    {
                        
    if (sorthandle.OutOfOrder(index))
                            sorthandle.Swap(index);
                        operations++;
                    }
                }
                
    return operations;
            }

        }
        
    public interface SortHandle
        {
             
    void Swap(int index);
             Boolean OutOfOrder(int index);
             
    int Length();
             
    void SetArray(object array);
        }
        
    public class IntSortHandle : SortHandle
        {
            
    private int[] array = null;
            
    public void Swap(int index)
            {
                
    int t = array[index];
                array[index] = array[index + 1];
                array[index + 1= t;
            }
            
    public  Boolean OutOfOrder(int index)
            {
                
    return (array[index] > array[index + 1]);
            }
            
    public void SetArray(object array)
            {
                
    this.array = (int[])array;
            }
            
    public int Length()
            {
                
    return array.Length;
            }
            
    public void PrintArray()
            {
                
    foreach (var a in array)
                {
                    Console.WriteLine(a);
                }
            }
        }

    上面,扮演Strategy中Context角色的BubbleSorter,包含了冒泡的具体算法。

    IntSortHandle 对BubbleSorter却是一无所知的,它不需要依赖于实现了冒泡排序算法的BubbleSorter。
    在TemplateMethod中,Swap和OutOfOrder的实现依赖于冒泡排序算法(DoubleBubblerSorter依赖于BubbleSorter)。

    而在 Strategy中,IntSortHandle 不需要依赖于BubbleSorter,所以我们可以在其他的排序中使用IntSortHandle

    同样,运行如下:

    IntSortHandle ibs = new IntSortHandle ();
    BubbleSorter2 bs2 = new BubbleSorter2(ibs);
    bs2.Sort(new int[] { 82315 });
    ibs.PrintArray();

    通过上面的例子我们可以看到Strategy模式的好处, 因为Strategy模式完全的遵守DIP原则,所以每个具体实现都可以被多个不同的通用算法操作。

    三、补充说明

    依赖倒置原则(DIP)

    DIP解释:
    1、高层模块不应该依赖于低层模块,二者都应该依赖于抽象。  
    2、抽象不应该依赖于细节。细节应该依赖于抽象。
    DIP中依赖于抽象的把握:
    1、任何变量都不应该持有一个指向具体来的引用。  
    2、任何类都不应该从具体来派生。  
    3、任何方法都不应该覆写它的任何基类中的已经实现的方法。
    我们在项目中的做法:
    每个较高层次都为它所需要的服务声明一个抽象接口,较低的层次实现了这些抽象接口。  
    每个高层类都通过该抽象接口使用下一层,这样高层就不依赖于低层。低层反而依赖于高层中声明的抽象服务接口。

    可以参考我之前写的一篇文章:谈谈我对DI的 理解

    四、参考资料

  • 相关阅读:
    HDU 1789 Doing Homework again(馋)
    Understanding and Using HRMS Security in Oracle HRMS
    BZOJ 1003 ZJOI2006 物流运输trans 动态规划+SPFA
    oracle常规任务
    征服OA 飞鱼工作流程的在线培训课程(两)HTML形成基于
    《iOS8 Swift编程指南》类书图像
    js css 实现简单的计算器
    Android四个基本组件(2)之Service 服务与Content Provider内容提供商
    Unity UGUI——Rect Transform包裹(Anchor Presets)
    NYOJ 1076 计划数(公式 要么 递归)
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/1807907.html
Copyright © 2011-2022 走看看