【手写源码-设计模式20】-观察者模式-基于初中文言文口技

1:主题拆解

①基本介绍

②初中文言文口技

③忽一人大呼"火起"

④观察者模式的优缺点

⑤适用场景

⑥寄语

2:基本介绍

         观察者模式:定义对象之间的一种一对多依赖关系,使得每一个对象状态发生改变时,其相关依赖对象皆得到通知并自动更新。

        观察者模式使用频率很高,用于建立一种对象之间的依赖关系,当一个对象发生改变时自动通知其他对象,其他对象将做出相应反应。在观察者模式中,发生改变的对象叫做观察目标,也叫被观察者,而被通知的对象叫做观察者。

        一个观察目标可以对应多个观察者,而且这些观察者之间没有任何相互关联,可以根据需要增加和删除观察者,使得系统便于扩展。

3:初中文言文口技

        我们再次体验一下童年阴影,口技,背诵全文....

口技_诗词_百度汉语

         京中有善口技者。会宾客大宴,于厅事之东北角,施八尺屏障,口技人坐屏障中,一桌、一椅、一扇、一抚尺而已。众宾团坐。少顷,但闻屏障中抚尺一下,满坐寂然,无敢哗者。

        遥闻深巷中犬吠,便有妇人惊觉欠伸,其夫呓语。既而儿醒,大啼。夫亦醒。妇抚儿乳,儿含乳啼,妇拍而呜之。又一大儿醒,絮絮不止。当是时,妇手拍儿声,口中呜声,儿含乳啼声,大儿初醒声,夫叱大儿声,一时齐发,众妙毕备。

        满坐宾客无不伸颈,侧目,微笑,默叹,以为妙绝。

        未几,夫齁声起,妇拍儿亦渐拍渐止。微闻有鼠作作索索,盆器倾侧,妇梦中咳嗽。宾客意少舒,稍稍正坐。

        忽一人大呼"火起",夫起大呼,妇亦起大呼。两儿齐哭。俄而百千人大呼,百千儿哭,百千犬吠。中间力拉崩倒之声,火爆声,呼呼风声,百千齐作;又夹百千求救声,曳屋许许声,抢夺声,泼水声。凡所应有,无所不有。虽人有百手,手有百指,不能指其一端;人有百口,口有百舌,不能名其一处也。于是宾客无不变色离席,奋袖出臂,两股战战,几欲先走。

        忽然抚尺一下,群响毕绝。撤屏视之,一人、一桌、一椅、一扇、一抚尺而已。 

4:忽一人大呼"火起"

 【手写源码-设计模式20】-观察者模式-基于初中文言文口技_第1张图片

 咱们废话不多说,用撸码实现口技高潮部分。

1:基础版

①定义角色类与方法

public class 一人
{
    public void 大呼()
    {
        Console.WriteLine("{0}大呼", this.GetType().Name);
        new 夫().起大呼();
        new 妇().起大呼();
        new 两儿().齐哭();
        new 百千人().大呼();
        new 百千儿().哭();
        new 百千犬().吠();
    }
}
public class 夫
{
    public void 起大呼()
    {
        Console.WriteLine("{0}起大呼", this.GetType().Name);
    }
}
public class 妇
{
    public void 起大呼()
    {
        Console.WriteLine("{0}起大呼", this.GetType().Name);
    }
}
public class 两儿
{
        public void 齐哭()
    {
        Console.WriteLine("{0}齐哭", this.GetType().Name);
    }
}
public class 百千人
{
    public void 大呼()
    {
        Console.WriteLine("{0}大呼", this.GetType().Name);
    }
}
public class 百千儿
{
    public void 哭()
    {
        Console.WriteLine("{0}哭", this.GetType().Name);
    }
}
public class 百千犬
{
    public void 吠()
    {
        Console.WriteLine("{0}吠", this.GetType().Name);
    }
}

②上端调用

一人 一人 = new 一人();
一人.大呼();

③运行结果

【手写源码-设计模式20】-观察者模式-基于初中文言文口技_第2张图片

分析:功能倒是实现了,但是一人这个对象,除了自己大呼,还有完成后续的很多职责。其实后面的很多职责和他没有任何关系。但是却又完全耦合在一起。

如果需要添加其他的声音,或者更改其中的顺序,则需要更改功能,进而引起系统的不稳定。

2:观察者模式版

①添加观察者接口

public interface IObserver
{
    void Action();
}

②每个对象类进项改造

public class 夫 : IObserver
{
    public void Action()
    {
        this.起大呼();
    }
    public void 起大呼()
    {
        Console.WriteLine("{0}起大呼", this.GetType().Name);
    }
}
public class 妇 : IObserver
{
    public void Action()
    {
        this.起大呼();
    }
    public void 起大呼()
    {
        Console.WriteLine("{0}起大呼", this.GetType().Name);
    }
}
public class 两儿 : IObserver
{
    public void Action()
    {
        this.齐哭();
    }
    public void 齐哭()
    {
        Console.WriteLine("{0}齐哭", this.GetType().Name);
    }
}
public class 百千人 : IObserver
{
    public void Action()
    {
        this.大呼();
    }
    public void 大呼()
    {
        Console.WriteLine("{0}大呼", this.GetType().Name);
    }
}
public class 百千儿 : IObserver
{
    public void Action()
    {
        this.哭();
    }
    public void 哭()
    {
        Console.WriteLine("{0}哭", this.GetType().Name);
    }
}
public class 百千犬 : IObserver
{
    public void Action()
    {
        this.吠();
    }
    public void 吠()
    {
        Console.WriteLine("{0}吠", this.GetType().Name);
    }
}

②观察对象改造

public class 一人
{ 
    private List _QbServerList = new List(); 
    public void AddObserver(IObserver observer)
    {
        this._QbServerList.Add(observer);
    } 
    public void RemoveObserver(IObserver observer)
    {
        this._QbServerList.Remove(observer);
    } 
    public void 一人ObServer()
    {
        Console.WriteLine("{0}大呼", this.GetType().Name);
        foreach (var observer in _QbServerList)
        {
            observer.Action();
        }
    }
}

③上端调用

一人 一人 = new 一人();
一人.AddObserver(new 夫());
一人.AddObserver(new 妇());
一人.AddObserver(new 两儿());
一人.AddObserver(new 百千人());
一人.AddObserver(new 百千儿());
一人.AddObserver(new 百千犬());
一人.一人ObServer();

③运行结果

【手写源码-设计模式20】-观察者模式-基于初中文言文口技_第3张图片

分析:以前的一人实例化以后,对应的职责就全部确定了,而现在可以动态控制后续职责。

观察者模式的角色:一堆的观察者,给这些观察着提供一个接口,此接口包括观察者的一个通用的动作。

观察对象(Suject),发生一些事后,会触发后续观察者的动作,(所有的观察着都在等在着事情的发生)。

Suject内部包括了一个观察者接口的集合,这个集合可以交给上端去自由的Add/Remove。

Suject的真实动作发生后,会去变量观察者接口的集合,将其中的动作执行一遍。

3:委托事件版

①观察对象添加事件

public class 一人
{
    //事件方式实现观察着模式
    public event Action 一人大呼Event;
    public void 大呼()
    {
        Console.WriteLine("{0} 大呼", this.GetType());
        if (this.一人大呼Event != null)
        {
                //一人大呼Event是一个多播委托,Invoke是依次执行每个委托对应的事件
                一人大呼Event.Invoke();
        }
    }
    //事件是维护一堆的方法。
}

②上端调用

一人 一人 = new 一人();
一人.一人大呼Event += new 夫().Action;
一人.一人大呼Event += new 妇().Action;
一人.一人大呼Event += new 两儿().Action;
一人.一人大呼Event += new 百千人().Action;
一人.一人大呼Event += new 百千儿().Action;
一人.一人大呼Event += new 百千犬().Action;
一人.大呼();

5:观察者模式的优缺点

1:优点

①分离表示层与逻辑层

定义了稳定的消息更新传递机制,并抽象了更新接口,使得可以有各种各样不同的表示层充当具体观察者角色

②降低耦合

在抽象目标以及抽象观察者之间建立了一个抽象耦合,观察目标只需要维持一个抽象观察者的集合,无须了解具体观察者,由于观察目标和观察者没有紧密耦合在一起,因此它们可以属于不同的抽象层次

③广播

观察者模式支持广播通信,观察目标会向所有已注册的观察者对象发送通知,简化了一对多的系统设计难度

④满足OCP

观察者模式满足开放闭合原则的要求,增加新的具体观察者无须修改原有系统代码,在具体观察者之间与观察目标之间不存在关联关系的情况下,增加新的观察目标很方便

2:缺点

①通知费时

如果有很多观察者,通知需要耗费较多时间。

②循环依赖导致崩溃

如果观察者模式与观察目标之间存在循环依赖,观察目标会导致触发它们之间进行循环调用,可能导致崩溃。

③不明确变化内容

观察者模式只是让观察者知道观察目标发生了变化,但是不知道变化的内容是什么。

6:适用场景

①一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两个方面封装在一个独立的对象中使它们可以独立地改变和复用。

②一个对象的改变将导致一个或多个其他对象也发生改变,而并不知道具体有多少对象发生改变,也不知道这些对象是谁。

③需要在系统中创建一个触发链,A对象的行为会影响B对象,B对象的行为会影响C对象,可以使用观察者模式创建一种链式触发机制。

7:寄语

【手写源码-设计模式20】-观察者模式-基于初中文言文口技_第4张图片

 有兴趣的小伙伴可以尝试对其中另外一段进行拆解分析实现。

如果您觉得文章写的还OK,记得收藏+关注哦,感谢!

大佬们还有其他好玩的场景吗?欢迎留言交流!

你可能感兴趣的:(设计模式,c#,设计模式,架构师)