单件 Singleton,单件的目标是保证一个类型只有一个实例,那么由谁来保证实例的唯一性呢?可能的方案有:
a)调用端保证。
调用端调用一个类时,他是不需要也不会去考虑这个类是否已经被实例化的。而且把这样的监管工作交给调用端是很不负责的做法。
b)类型内部保证。
类型内部如何保证?
将实例创建工作放到类型内部,这样类型就可以将实例创建工作监管起来。类型可以知道内部的实例有没有被创建,甚至可以知道创建实例的工作被执行了多少次。
所以个人认为理解单件需要分为两步:
1、 监管工作谁来做?实例的监管工作需要类型自己去做。
2、 监管工作如何做?类型如何保证实例唯一就是技术实现问题了,可以看到的版本有 线程安全的、双重锁定的、延迟初始化的等。
下面使用伪代码逐步分析实例化工作放到类型内部的做法。
调用我,实例我给你
{
Singleton Instance = null;
// 实例化类型 Singleton
Singleton GetInstance()
{
Instance = new Singleton();
return Instance;
}
}
你只管调用,我保证唯一
{
Singleton Instance = null;
//实例化类型 Singleton
Singleton GetInstance()
{
Instance = new Singleton();
return Instance;
}
// 实例化类型 Singleton,实例化时判断类型有没有被创建过,这样就保证了实例的唯一
Singleton GetInstance()
{
if (Instance == null)
{
Instance = new Singleton();
}
return Instance;
}
}
你们都可以调用,我需要统计调用次数
{
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 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 写法发出来,省的大家再去查资料。
{
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
{
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。
运行一下
bs.Sort(new double[] { 1, 2.2, 3, 4, 2.1, 3.5, 3.8, 4.5, 1.6 });
bs.PrintArray();
2、Strategy
{
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 。
同样,运行如下:
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、任何方法都不应该覆写它的任何基类中的已经实现的方法。
我们在项目中的做法:
每个较高层次都为它所需要的服务声明一个抽象接口,较低的层次实现了这些抽象接口。
每个高层类都通过该抽象接口使用下一层,这样高层就不依赖于低层。低层反而依赖于高层中声明的抽象服务接口。
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。
适用场景:
许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。
需要使用一个算法的不同变体。
算法使用客户不应该知道的数据。使用策略模式避免暴露复杂的、与算法相关的数据结构。
一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入他们各自的Strategy类中以代替这些条件语句。
2. 基本的实现代码
上述代码类图表示为:
调用者的代码:
优点
Strategy类层次定义了一系列的可供重用的算法或行为;
消除条件语句;
Strategy提供相同行为的不同实现;
缺点
客户必须了解不同的Strategy;
Strategy和Context之间的通信开销;
增加了对象的数目;
1. 特点说明
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
各子类中公共的行为被提取出来并集中到一个公共父类中,避免代码重复。
注:Template Method模式和C++模板一点关系都没有
2. 基本代码实现
上述代码类图表示如下:
调用者代码:
3. Template Method模式附加说明:
当不变的和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现。
我们通过模板方法模式,把这些行为搬移到单一的地方,这样就可以帮助子类摆脱重复的不变行为的纠缠。
三、Strategy模式和Template Method模式的比较:
1. Strategy模式
定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法的变化可独立于使用它的客户。
使用委托来改变整个算法。
2. Template Method模式
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
使用继承来改变算法的一部分。
评论