# 【笔记】大话设计模式21-23

【笔记】大话设计模式21-23

单例模式

21.1 Example

这个很好理解,每个人都是唯一的实体,不允许影分身术,分裂成多个你出来。

所以,电脑和手机上,很多软件,都只能打开1个窗口,重复打开,也只会调用那个唯一的单例。

一般C#里,定义一个全局静态变量即可,每次判断这个变量有没有存在或者窗体有没有Disposed,存在则已经生成过单例了。

picture source : Refactoring.Guru

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

在阿三还在上大学的时候,那时候手机还是诺基亚、摩托罗拉等品牌兴盛的时候,那时候,不同手机的软件不能通用,需要去手机品牌官网指定的网站上下载,哪怕下载同一款软件。

现在安卓机就是这样,大家都是使用安卓的操作系统,接口都一样的,想要开发软件,就在统一接口下,开发自己的软件或者适配自己的机型就行了。

picture source : Refactoring.Guru

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对象的一部分;

  • 合成表示一种强的拥有关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样;

  • 桥接模式就是实现系统可能有多角度分类,每一种分类可能都有变化,就把多角度分离出来让其对立变化,减少耦合。就像电脑硬盘、显卡、内存条等设备,都可以独立插拔安装,选用不同品牌的硬件。

    picture source : Refactoring.Guru

    其实就是实现上图的思维模式转变,将原本通过继承等耦合方式实现的重量子类,变成独立的维度类,降低耦合,同时增加了每个维度的类的扩展空间。

23 命令模式

亦称: 动作、事务、Action、Transaction、Command

23.1 Example

大头家旁边开了一家烧饼店,店家是小本经营,就一个摊位,没有座位等,大家排队买烧饼,买完就带回去吃。

但是这样有个缺陷,就是大家围成一圈买烧饼,纷纷告诉老板要什么口味的烧饼,老板一个人也记不住,很容易把刚烤好的烧饼给了后来的人,先来的人没买到,导致了和老板的冲突。

这就是“行为请求者”(买烧饼的人)和“行为实现者”(烧饼店老板)的紧耦合。

反观看有点餐机的店铺就不同了,每个客人点餐后,点餐请求加入到请求队列中,而且包含详细的点餐内容,店面老板按照请求队列做餐即可。老板都不用知道谁点的什么内容。这就实现了解耦。

picture source : Refactoring.Guru

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 总结

  • 优点:
    • 命令行模式能较容易地设计一个命令队列;
    • 较容易地将命令记入日志;
    • 允许接受请求的一方决定是否否决请求;
    • 容易实现对请求的撤销、重做和延迟执行,比如数据回滚操作;
    • 加入新命令不影响其他类;
    • 将请求操作的对象与指导执行对象分开。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值