立面图案结合观察者模式

时间:2015-11-25 10:21:43

标签: c# design-patterns

我得到了一个任务,以确切了解外观模式是什么。我已经google了一下,发现它是为了通过制作一个"接口"来保护客户端免受非常复杂的系统的影响。所以我有几个问题,我在多个例子中看到它们是复杂系统的C#接口,但我也看到一些使用A类作为"接口" (见here)。我只能理解它,如果它是一个基类,简化了对不同类的许多不同的复杂方法调用,如(例如here中的银行示例)

  1. 所以我的第一个问题是,如果我是正确的,你将实现"接口"作为一个班级?

  2. 我的另一个问题是,你能否将外观模式与观察者模式一起使用。 Facade类将观察所有主题,然后根据主题控制不同类中应该调用哪些方法?

  3. 编辑:根据要求,我尝试使用外观为观察者模式创建一个示例项目,结果如下:

    public class Facade
    {
    
        private Dictionary<ISubject, List<IObserver>> Subjects { get; set; }
    
        public Facade()
        {
            Subjects = new Dictionary<ISubject, List<IObserver>>();
        }
    
        public void AddObserverToSubject(ISubject sub,IObserver obs)
        {
            if (Subjects.ContainsKey(sub))
                Subjects[sub].Add(obs);
            else
            {
                List<IObserver> observers = new List<IObserver>();
                observers.Add(obs);
                Subjects.Add(sub, observers);
            }
            obs.Subject = sub;
        }
    
        public void DeleteObserverFromSubject(IObserver obs,ISubject subject)
        {
            Subjects[subject].Remove(obs);
        }
        public void Notify(ISubject subject)
        {
            foreach (var observer in Subjects[subject])
            {
                observer.Update();
            }
    
        }
    }
    
    public interface ISubject
    {
        Facade Observers { get; set; }
        int GetState();
        void SetState(int state);
    }
    
    public interface IObserver
    {
        ISubject Subject { get; set; }
        void Update();
        string Mood { get; }
    }
    

    所以每个观察者都会根据主题的内容来更新他们的情绪。

    我已经完成了IObserver和ISubject的两个实现,但我只会在这里展示其中一个。

    public class TeacherObserver : IObserver
    {
        public ISubject Subject { get; set; }
        private int _currentSalery = 500;
        public string Mood { get; private set; }
        public TeacherObserver()
        {
            Mood = "Happy";
        }
    
    
        public void Update()
        {
            var newSalery = Subject.GetState();
            if (_currentSalery < newSalery)
            {
                Mood = "Happy";
            }
            else
            {
                Mood = "Sad";
            }
            _currentSalery = newSalery;
        }
    }
    
    public class SalerySubject :ISubject
    {
        public Facade Observers { get; set; }
        private int _salery;
        public int GetState()
        {
            return _salery;
        }
    
        public void SetState(int state)
        {
            _salery = state;
            Observers.Notify(this);
        }
    }
    

    所以我喜欢这个的一点是,主题并不需要知道绑定到它的所有观察者(现在这将由facade类处理)。但是从客户看来,这几乎与他必须做的一样:

    class Program
    {
        static void Main(string[] args)
        {
            Facade.Facade observer = new Facade.Facade();
            ISubject salery = new SalerySubject();
            IObserver teacher = new TeacherObserver();
            salery.Observers = observer;
            observer.AddObserverToSubject(salery,teacher);
            Console.WriteLine("Teacher is " + teacher.Mood);
            salery.SetState(100);
            Console.WriteLine("Teacher salery just went down. The teacher is now " + teacher.Mood);
        }
    }
    
    1. 这让我觉得用门面做这件事真的没有意义,因为门面的重点是让客户更容易吗? (或隐藏信息)或我错了吗?

4 个答案:

答案 0 :(得分:2)

  

我得到了一项任务,以确切了解外观模式是什么

Facade只是另一个包装纸 它包装了一些实体,通常是为了隐藏客户端的一些细节。

  

如果我是正确的,你会把“接口”作为一个类实现吗?

接口是一份合同。

要实现此合同,您需要一个类(因为我们正在谈论设计模式,我省略了结构讨论)。换句话说,无法仅使用接口实现 Facade,因为您需要一个类,其中将放置实现逻辑,但接口可以帮助您使组件松散耦合。

实际上,使用界面或不使用界面与特定模式无关。

  

你可以将外观模式与观察者模式一起使用

理论上 - 是的,你可以。在实践中 - 这取决于。

答案 1 :(得分:2)

我认为术语“界面”是指&#39;用法有不同的含义。你有一个&#39;界面的通用术语&#39;访问它,你有一个具体的术语&#c; c#接口定义&#39;作为那种语言的一部分。所以我认为,在你的报价中

  

通过制作&#34;界面&#34;

来保护客户端免受非常复杂的系统的攻击

使用第一个通用含义。问题是如何构建它:

  

1.所以,我的第一个问题是,如果我是正确的,你将实施&#34;界面&#34;作为一个班级?

我会同时使用两者。首先,我将创建一个c#接口来定义契约,然后构建一个基类作为参考实现。如果你只使用一个基类,那么所有其他可能的类都必须继承这个,他们只得到实现细节,因为他们想要合同。 如果其他可能的类可以使用您的接口,则只需要实现它,这意味着它们必须在接口定义中提供方法并且彼此之间没有链接。

  

2.我的其他问题是,您是否可以将外观模式与观察者模式一起使用。门面课将观察所有   科目,然后控制不同类中的方法应该是什么   被叫,取决于主题?

是的,你可以,但随后所有课程的观察行为都是&#39; part&#39;外立面。如果那就是你想要的,好的。

  

3.这让我觉得用立面做这件事真的没有意义,因为立面的全部要点就是制作它   对客户来说更容易吗? (或隐藏信息)或我错了吗?

我认为为其定义的目的设置一个外观是有意义的,以便连接它背后的复杂系统。只有当观察行为没有任何内容时,才有隐藏的复杂性。

所以我可以建议像这样重新设计一下:

我会在事件中实施ISubject,因为它不需要知道谁在观察,它只会通知:

public interface ISubject
{
    event EventHandler OnNotify;
}

然后创建第二个界面以获取薪水:

public interface ISalerySubject: ISubject
{
    int Salery { get; set; }
}

IObserver可以容纳ISubject

public interface IObserver
{
    ISubject Subject { get; set; }
}

现在让我们具体化吧。类SalerySubject正在实现接口ISalerySubject,因此当更改销售时会触发事件:

public class SalerySubject : ISalerySubject
{
    public event EventHandler OnNotify;

    private int salery;
    public int Salery
    {
        get { return salery; }
        set
        {
            salery = value;
            if (OnNotify != null) OnNotify(this, new EventArgs());
        }
    }

}

班级TeacherObserver正在实施界面IObserver,并将其方法Update绑定到ISubject的事件:

public class TeacherObserver : IObserver
{
    private int _currentSalery = 500;
    public string Mood { get; private set; }

    public ISubject subject;
    public ISubject Subject
    {
        get { return subject; } 
        set
        {
            // Relase old event
            if (subject != null) subject.OnNotify -= Update;

            subject = value;

            // Connect new event
            if (subject != null) subject.OnNotify += Update;
        } 
    }

    public TeacherObserver()
    {
        Mood = "Happy";
    }


    public void Update(object sender, EventArgs e)
    {
        ISalerySubject SalerySubject = Subject as ISalerySubject;
        if (SalerySubject != null)
        {
            var newSalery = SalerySubject.Salery;
            if (_currentSalery < newSalery)
            {
                Mood = "Happy";
            }
            else
            {
                Mood = "Sad";
            }
            _currentSalery = newSalery;
        }
    }
}

现在你可以使用它了:

class Program
{
    static void Main(string[] args)
    {
        ISalerySubject salery = new SalerySubject();
        TeacherObserver teacher = new TeacherObserver();
        teacher.Subject = salery;

        Console.WriteLine("Teacher is " + teacher.Mood);
        salery.Salery = 100 ;
        Console.WriteLine("Teacher salery just went down. The teacher is now " + teacher.Mood);
    }
}   

所以,到目前为止,不需要立面。也许你想保留一个观察者列表,但通常不存储在一个外观中。也许你的系统要复杂得多,所以无论如何都有一个很好的理由。

答案 2 :(得分:1)

外观模式是一种结构设计模式。 https://sourcemaking.com/design_patterns/facade

façade用于将应用程序(或模型)的基础逻辑与用户界面分开。它的目的是分离关注点和协助简化底层类结构的使用。

façade本身是一个抽象类,它列出了可以为该接口实现的方法,以及这些方法如何连接到模型的底层类结构。具体实现是底层模型类的实际创建,对象以及使用这些对象实例来运行程序。一直以来,底层程序逻辑的过程只能通过外观调用,并且与最终用户无关。

enter image description here

观察者模式是一种行为设计模式。 https://sourcemaking.com/design_patterns/observer

观察者模式链接一个对象及其所有依赖项。因此,当对象发生任何更改时,所有链接的依赖项也将受此更改的影响。它是MVC应用程序中使用的模式之一。模型/对象维护所有业务逻辑;这被传递给控制器​​/观察者。然后,控制器根据用户对对象所做的更改来更新和维护视图。

控制器/观察者将编程逻辑的关注点与用户分开,就像外观将其与用户分开一样。控制器/观察者与模型和视图是不可分离的。它依赖于对两者的访问来充当两者之间的外行解释器,将模型逻辑解析为用户界面,反之亦然。

enter image description here

说实话,我没有很好地跟着你。我认为你将收藏的想法与设计模式混为一谈。

在我看来,混合两者的逻辑会混淆使用任何一种模式背后的编程逻辑。虽然设计模式可能不是很明确,但这是一个特殊的例子,我不能(在我的头脑中)想到一个有用的应用程序。如果有有用的例子,我相信有人会说清楚。

答案 3 :(得分:1)

是的,我同意你对外观的理解 - Facade的目的是提供一个 new&amp;简化接口(或接入点或网关)到复杂系统。或者统一界面来隐藏多个子系统。

现在,我将逐一回答您的问题 -

  

所以我的第一个问题是,如果我是正确的,你将实施   “界面”作为一个阶级?

当我们在外观的上下文中使用“interface”时,它指的是 API ,表达了 - 交互点联系人的含义。外观在客户端和客户端之间充当联系人。复杂的系统。

现在,如何实现立面?当然,您可以使用c#interface来定义Facade将向客户端公开的函数,事件和属性。 实际的包装器将是class实现此interface

即使您隐藏的复杂系统是静态的,在实现单元测试时,在实现class的{​​{1}}中包装静态系统也会有所帮助。

  

我的另一个问题是,你能否同时使用门面模式   观察者模式。门面课将在所有科目上观察   然后控制应该调用不同类中的哪些方法,   取决于主题?

从语言纯度的角度来看,外观应该只在已包装的子系统的现有API上定义新的API。

如果您隐藏的系统需要发布者 - 订阅者模型,则必须在外观中实现观察者模式,包装子系统的发布者 - 订阅者调用。

但是,不要以简单的名义为立面添加更多的职责或逻辑,将其视为子系统的一个相当简单的倡导者或推动者,仅此而已。它不应该成为一个全知的“上帝”对象。

  

这让我觉得这样做并没有多大意义   有一个立面,因为立面的整个点是使它更容易   对于客户权利? (或隐藏信息)或我错了吗?

在您的示例中,所有这些逻辑都不应该在外观中。我同意它会让客户更容易,但它不是建立在包装的子系统上,而是像门面提供的附加服务。 因此,从外观进入一个单独的类,客户端可以使用该类来使其生活轻松。

如果您认为外立面中有任何东西可以从外立面中取出而不暴露子系统,则必须将该外观取出外观。

希望它有所帮助。如果有任何不清楚或者你认为我错过了你的任何问题,请给我写评论。我们可以从那里拿走它。