【笔记】大话设计模式21-23
文章目录
单例模式
21.1 Example
这个很好理解,每个人都是唯一的实体,不允许影分身术,分裂成多个你出来。
所以,电脑和手机上,很多软件,都只能打开1个窗口,重复打开,也只会调用那个唯一的单例。
一般C#里,定义一个全局静态变量即可,每次判断这个变量有没有存在或者窗体有没有Disposed,存在则已经生成过单例了。
21.2 定义
单例模式(Singleton)
:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
21.3 Show me the code
一般单例代码(懒汉模式)
代码中lock (syncRoot)
是将当前变量锁定,阻塞其他线程;
该锁前后进行了2次判断,是因为如果在多线程程序中,两个进行同时判断了第一个if
语句,则会同时进入加锁代码中,因为lock
机制,只允许1个线程进入,另一个阻塞,所以当第一个线程创建实例后,第二个线程即便在lock
中,也不会再次创建实例。
这种做法就是Double-Check Locking
(双重锁定)。
class Singleton
{
// 私有静态变量
private static Singleton instance;
// 初始化一个锁变量
private static readonly object syncRoot = new object();
// 私有构造函数,不允许外部实例化
private Singleton()
{
}
public static Singleton GetInstance()
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
静态初始化(饿汉模式)
C#与公共语言运行库提供了“静态初始化”的方法,不需要编写线程安全代码,即可解决多线程环境下不安全问题。
解决了2个问题:全局访问、实例化控制
instance变量标记为readonly,只能在静态初始化期间或在类构造函数中分配变量,实例被加载时就将自己实例化。
// sealed 阻止派生类,因为派生类可能会增加实例
public sealed class Singleton
{
// 在第一次引用类的任何成员时创建实例。公共语言运行库负责处理变量初始化
private static readonly Singleton instance = new Singletion();
private Singleton(){ }
public static Singleton GetInstance()
{
return instance;
}
}
21.4 总结
- 饿汉模式:静态初始化的方式,类一加载就实例化对象,需要提前占用系统资源。
- 懒汉模式:面临多线程访问的安全性问题,需要双重锁定,需要的时候才实例化。
- 抽象工厂模式、生成器模式和原型模式都可以用单例实现。
22 桥接模式
22.1 Example
在阿三还在上大学的时候,那时候手机还是诺基亚、摩托罗拉等品牌兴盛的时候,那时候,不同手机的软件不能通用,需要去手机品牌官网指定的网站上下载,哪怕下载同一款软件。
现在安卓机就是这样,大家都是使用安卓的操作系统,接口都一样的,想要开发软件,就在统一接口下,开发自己的软件或者适配自己的机型就行了。
22.2 定义
合成/聚合复用原则(CARP)
:尽量使用合成/聚合,尽量不要使用类继承。桥接模式(Bridge),将抽象部分与它的实现部分分离,使它们可以独立地变化。
22.3 Show me the code
class Program
{
static void Main(string[] args)
{
Abstraction ab = new RefinedAbstraction();
ab.SetImplementor(new ConcreteImplementorA());
ab.Operation();
ab.SetImplementor(new ConcreteImplementorB());
ab.Operation();
Console.Read();
}
}
class Abstraction
{
protected Implementor implementor;
public void SetImplementor(Implementor implementor)
{
this.implementor = implementor;
}
public virtual void Operation()
{
implementor.Operation();
}
}
class RefinedAbstraction : Abstraction
{
public override void Operation()
{
implementor.Operation();
}
}
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的方法执行");
}
}
22.4 总结
-
聚合表示一种弱的拥有关系,体现A对象可以包含B对象,但是B对象不是A对象的一部分;
-
合成表示一种强的拥有关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样;
-
桥接模式就是实现系统可能有多角度分类,每一种分类可能都有变化,就把多角度分离出来让其对立变化,减少耦合。就像电脑硬盘、显卡、内存条等设备,都可以独立插拔安装,选用不同品牌的硬件。
其实就是实现上图的思维模式转变,将原本通过继承等耦合方式实现的重量子类,变成独立的维度类,降低耦合,同时增加了每个维度的类的扩展空间。
23 命令模式
亦称: 动作、事务、Action、Transaction、Command
23.1 Example
大头家旁边开了一家烧饼店,店家是小本经营,就一个摊位,没有座位等,大家排队买烧饼,买完就带回去吃。
但是这样有个缺陷,就是大家围成一圈买烧饼,纷纷告诉老板要什么口味的烧饼,老板一个人也记不住,很容易把刚烤好的烧饼给了后来的人,先来的人没买到,导致了和老板的冲突。
这就是“行为请求者”(买烧饼的人)和“行为实现者”(烧饼店老板)的紧耦合。
反观看有点餐机的店铺就不同了,每个客人点餐后,点餐请求加入到请求队列中,而且包含详细的点餐内容,店面老板按照请求队列做餐即可。老板都不用知道谁点的什么内容。这就实现了解耦。
23.2 定义
命令模式(Command)
:将一个请求封装成一个对象,从而使你可以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
23.3 Show me the code
class Program
{
static void Main(string[] args)
{
//开店前的准备
Barbecuer boy = new Barbecuer();
Command bakeMuttonCommand1 = new BakeMuttonCommand(boy);
Command bakeMuttonCommand2 = new BakeMuttonCommand(boy);
Command bakeChickenWingCommand1 = new BakeChickenWingCommand(boy);
Waiter girl = new Waiter();
//开门营业 顾客点菜
girl.SetOrder(bakeMuttonCommand1);
girl.SetOrder(bakeMuttonCommand2);
girl.SetOrder(bakeChickenWingCommand1);
//点菜完闭,通知厨房
girl.Notify();
Console.Read();
}
}
//服务员
public class Waiter
{
private IList<Command> orders = new List<Command>();
//设置订单
public void SetOrder(Command command)
{
if (command.ToString() == "命令模式.BakeChickenWingCommand")
{
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();
}
}
}
//抽象命令
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 BakeChickenWingCommand : Command
{
public BakeChickenWingCommand(Barbecuer receiver)
: base(receiver)
{ }
public override void ExcuteCommand()
{
receiver.BakeChickenWing();
}
}
//烤肉串者
public class Barbecuer
{
public void BakeMutton()
{
Console.WriteLine("烤羊肉串!");
}
public void BakeChickenWing()
{
Console.WriteLine("烤鸡翅!");
}
}
23.4 总结
- 优点:
- 命令行模式能较容易地设计一个命令队列;
- 较容易地将命令记入日志;
- 允许接受请求的一方决定是否否决请求;
- 容易实现对请求的撤销、重做和延迟执行,比如数据回滚操作;
- 加入新命令不影响其他类;
- 将请求操作的对象与指导执行对象分开。