设计模式研究

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

设计模式研究(二)-Singleton 

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

两种模式理念上的差别

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

设计模式研究_第1张图片

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

 设计模式研究_第2张图片

 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 。

设计模式研究_第3张图片 

     // 引入适配器   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 {  get set ; }
        
// 实例化类型 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 {  get set ; }
        
// 实例化类型 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、模板方法

 设计模式研究_第4张图片

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

 2、策略

设计模式研究_第5张图片

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

二、实例比较

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 [] {  1 2.2 3 4 2.1 3.5 3.8 4.5 1.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 [] {  8 2 3 1 5  });
ibs.PrintArray();

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

三、补充说明

依赖倒置原则(DIP)

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

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

四、参考资料

你可能感兴趣的:(设计模式)