【问题标题】:Facade pattern combined with observer pattern外观图案与观察者图案相结合
【发布时间】:2015-11-25 10:21:42
【问题描述】:

我接到了一项任务,要弄清楚外观模式到底是什么。我用谷歌搜索,发现它是为了通过制作一个“接口”来保护 client 免受非常复杂的系统的影响。所以我有几个问题,我在多个示例中看到他们为复杂系统创建了 C# 接口,但我也看到了一些使用 A 类作为“接口”的示例(如 here 所示)。如果它是一个基类,它可以简化对不同类的许多不同的复杂方法调用,我只能理解它(银行示例见here

  1. 所以我的第一个问题是我是否正确,您将“接口”作为一个类来实现?

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


编辑:根据要求,我尝试为观察者模式制作一个带有外观的示例项目,结果如下:

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);
    }
}

所以我喜欢这个的一件事是主体不必知道绑定到它的所有观察者(这将由外观类现在处理)。但从客户的角度来看,这与他必须拨打的电话几乎相同:

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. 这让我觉得用外观做这件事并没有什么意义,因为外观的全部意义在于让客户更容易,对吧? (或隐藏信息)还是我错了?

【问题讨论】:

    标签: c# design-patterns


    【解决方案1】:

    我认为“接口”一词有不同的含义。您可以使用通用术语“某物的接口”来访问它,并且您可以使用具体术语“c# 接口定义”作为该语言的一部分。所以我认为,在你的报价中

    通过制作“接口”来保护客户免受非常复杂的系统的影响

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

    1.所以我的第一个问题是我是否正确,您将“接口”作为一个类来实现?

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

    2.我的另一个问题是,你能将外观模式与观察者模式一起使用吗?外观类将观察所有 科目,然后控制不同类中的方法应该是什么 叫什么,取决于主题?

    是的,你可以,但是所有类的观察行为都是外观的“一部分”。如果这是你想要的,好的。

    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 接口,所以当salery 发生变化时,会触发事件:

    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);
        }
    }   
    

    因此,到目前为止,还不需要外观。也许您想保留一个观察者列表,但通常不存储在外观中。也许您的系统要复杂得多,所以无论如何,外观都是有充分理由的。

    【讨论】:

    • 好吧,我明白你的意思了,观察者模式还不够复杂。你可以添加一个例子吗?可以只是一个链接到某种很好的例子,感谢到目前为止的回答!
    • 我实际上找到了一个非常好的外观类比。因此,假设您有一个带有通用遥控器的家庭影院。当您单击此遥控器上的“打开”时,它必须打开您的电视,调暗灯光,打开音响系统(可能配置了“打开音量”)所有这些您都可以放入外观类等等用户(客户端)只需要使用 On 和 Off 方法
    • 在 .Net 4 中,有内置类 'IObserver' 和 'IObservable'。也许你想看看那个。
    【解决方案2】:

    我得到了一项任务,要找出外观模式的确切含义

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

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

    接口是契约。

    要实现这个契约,你需要一个类(因为我们讨论的是设计模式,所以我省略了结构的讨论)。换句话说,您不能仅使用接口来实现外观,因为您需要一个类,其中将放置实现逻辑,但接口可以帮助您使组件松散耦合。

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

    你能把外观模式和观察者模式一起使用吗

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

    【讨论】:

    • 当你说在实践中取决于,你到底是什么意思?感谢您的回答!
    • @Sumsar1812:我的意思是,您需要发布更具体的示例。仅在操作设计模式时很难回答。
    • 我对OP问题的理解是,他所说的“接口”是指“充当复杂类型接口的外观类”。我不认为他的意思是“接口”,即合同。希望这会有所帮助
    • 好吧,我试着做一个例子,然后把它贴在这里,明天就可以了
    • 我现在在问题中添加了一个示例
    【解决方案3】:

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

    外观将应用程序(或模型)的底层逻辑与用户界面分开。它用于分离关注点并有助于简化底层类结构的使用。

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

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

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

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


    说实话,我没有很好地遵循你的例子。我认为您将集合的概念与设计模式混淆了。

    在我看来,将两者的逻辑混合使用会混淆使用任一模式背后的编程逻辑。尽管设计模式可能不是很明确,但这是我无法(不由自主地)想到一个有用的应用程序的特定示例。如果有有用的例子,我相信有人会提出来。

    【讨论】:

      【解决方案4】:

      是的,我同意你对外观的理解——外观的目的是为复杂系统提供一个新的和简化的接口(或接入点或网关)。或者一个统一的界面来隐藏多个子系统。

      现在,我会一一回答你的问题——

      所以我的第一个问题是我是否正确,您将实施 “接口”作为一个类?

      当我们在门面上下文中使用“接口”时,它指的是API,传达了-交互点接触点时间>。立面充当客户端和复杂系统之间的联系点

      现在,如何实现外观?当然,您可以使用 c#interface 来定义外观将向客户端公开的功能、事件、属性。 实际的包装器将是一个class 实现这个interface

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

      那么我的另一个问题是,你能将外观模式与 观察者模式。外观类将观察所有主题 然后控制应该调用不同类中的哪些方法, 取决于主题?

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

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

      但是,不要以简单的名义向外观添加更多的职责或逻辑,将其视为子系统的相当简单的倡导者或促进者,仅此而已。不应该成为无所不知的“神”物。

      这让我觉得这样做没有任何意义 带有立面,因为立面的全部意义在于使它更容易 为客户对吗? (或隐藏信息)还是我错了?

      在您的示例中,所有这些逻辑都不应该在外观中。我同意它会让客户更容易,但它不是建立在包装子系统上的,它就像外观提供的附加服务。 因此,将其从外观中取出到一个单独的类中,客户端可以使用该类来简化其工作。

      如果立面中有任何你认为可以从立面中取出而不暴露子系统的东西,那么该东西必须从立面中取出。

      希望对您有所帮助。如果有任何不清楚的地方或者您认为我遗漏了您的任何问题,请给我写评论,我们可以从那里开始。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-07-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多