外观模式与观察者模式的结合

4
我得到了一个任务,需要找出外观模式是什么。我已经搜索过了,发现它的作用是通过创建一个“接口”来保护客户端免受非常复杂的系统的影响。所以我有几个问题,我在多个示例中看到的是他们为复杂系统制作了一个C#接口,但我也看到一些人使用类作为“接口”(如这里所示)。只有当它是一个基类,将许多不同的复杂方法调用简化为不同的类时,我才能理解它(如银行示例中所示,在这里)。
  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);
    }
}

这让我想到,使用门面模式好像并没有太大的意义,因为门面模式的整个目的就是使客户端更容易使用(或隐藏信息),难道我错了吗?
4个回答

3
我认为术语“接口”有不同的含义。你可以使用“接口”这个通用术语来访问某些内容,也可以使用具体的术语“c#接口定义”,作为该语言的一部分。因此,在你的引用中,“通过创建一个“接口”来保护客户端免受非常复杂的系统影响”中使用了第一个通用含义。问题是如何构建它:
我的第一个问题是,如果我正确的话,您会将“接口”实现为一个类吗?我会同时使用两者。首先,我会创建一个c#接口来定义合同,然后构建一个基类作为参考实现。如果只使用一个基类,则所有其他可能的类都必须从此继承,并且它们仅因为想要合同而获得实现细节。如果其他可能的类可以使用您的接口,则它们只需实现它,也就是说,它们必须提供接口定义中的方法,并且彼此没有关联。
我的另一个问题是,门面模式和观察者模式能否一起使用。门面类将观察所有主题,然后控制应调用不同类中的哪些方法,具体取决于主题。是的,你可以,但是观察行为及其所有类与门面的“部分”相关。如果这是你想要的,那么可以。
这让我认为,使用门面不太合理,因为门面的整个目的是为了使客户端更容易使用(或隐藏信息),或者我错了?我认为,对于它所定义的目的,即将其与其背后的复杂系统进行接口连接,使用门面是有意义的。只有当没有观察行为时,才没有需要隐藏的复杂性。
因此,我可以建议进行一些小的重新设计,例如:我会使用事件来实现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);
    }
}   

因此,在这个阶段,没有必要使用门面模式。也许你想保留观察者列表,但通常不会存储在门面中。如果你的系统更加复杂,那么使用门面模式也是有好处的。


好的,我明白你的意思了,观察者模式不够复杂。你能否添加一个例子来说明一下呢?可以是某种好例子的链接,非常感谢你迄今为止的回答! - Sumsar1812
1
我实际上找到了一个非常好的外观模式类比。假设你有一个带有通用遥控器的家庭影院。当你在遥控器上点击“开”时,它将打开你的电视,调暗灯光,打开声音系统(也许是配置好的“开启音量”)。所有这些都可以放入一个外观模式类中,因此用户(客户端)只需要使用“开”和“关”方法即可。 - Sumsar1812
1
在 .Net 4 中,有内置的类 'IObserver<T>' 和 'IObservable<T>'。也许你想看看这个。 - Dieter Meemken

2
我得到了一个任务,需要确定外观模式是什么。 外观模式只是另一种包装方式。它通常包装某个实体,以向客户端隐藏某些细节。
如果我正确的话,你会把“接口”实现为一个类吗? 接口是一份合同。 要实现这个合同,您需要一个类(由于我们正在谈论设计模式,我省略了结构讨论)。换句话说,您不能仅使用接口来实现外观,因为您需要一个类,在其中放置实现逻辑,但接口可以帮助您使组件松散耦合。 实际上,是否使用接口与特定模式无关。
你能和观察者模式一起使用外观模式吗? 理论上-是的,你可以。实际上-这取决于具体情况。

当你说实践中会有所不同时,你具体是什么意思?感谢回答! - Sumsar1812
@Sumsar1812:我的意思是,你需要发布更具体的例子。仅在操作设计模式时回答很困难。 - Dennis
我理解 OP 的问题是指“接口”,他的意思是“作为复杂类型接口的外观类”。我认为他不是指“接口”即契约。希望这可以帮到你。 - GinjaNinja
好的,我会尝试编写一个示例并在此之后发布它,可能会在明天。 - Sumsar1812
我现在已经在问题中添加了一个示例。 - Sumsar1812

1
外观模式是一种结构性设计模式。它的作用是将应用程序(或模型)的基础逻辑与用户界面分离。它的目的是分离关注点并协助简化底层类结构的使用。

外观本身是一个抽象类,它规定了可以为该接口实现哪些方法以及这些方法如何连接到模型的底层类结构。具体实现是将底层模型类创建为对象,并使用这些对象实例来运行程序。在此过程中,底层程序逻辑的处理仅通过外观调用,对最终用户而言不相关。https://sourcemaking.com/design_patterns/facade

enter image description here

观察者模式是一种行为设计模式。它将一个对象与其所有依赖项链接起来。因此,当对象发生任何更改时,所有链接的依赖项也会受到影响。它是MVC应用程序中使用的模式之一。模型/对象维护所有业务逻辑;这被传递给控制器/观察者。然后,控制器根据用户对对象所做的更改更新和维护视图。控制器/观察者将编程逻辑与用户分开,就像外观将其与用户分开一样。控制器/观察者与模型和视图密不可分。它依赖于访问两者,充当两者之间的门户,将模型逻辑解析为用户界面,反之亦然。

enter image description here


说实话,我没有很好地跟随你的例子。我认为你混淆了集合和设计模式的概念。在我看来,将两者的逻辑混合起来会使使用任一模式的编程逻辑变得混乱。虽然设计模式可能不是非常明确,但这个特定的例子我不能(脑海中)想出一个有用的应用。如果有有用的例子,我相信有人会提出来的。

1

是的,我同意你对Facade的理解 - Facade的意图是为复杂系统提供一个新的和简化的接口(或访问点或网关)。或者提供一个统一的接口来隐藏多个子系统。

现在,我会逐个回答你的问题 -

我的第一个问题是,如果我正确的话,您会将“接口”实现为一个类吗?

当我们在Facade的上下文中使用“接口”时,它指的是API,传达着- 交互点联系点的含义。外观充当客户端与复杂系统之间的接触点

现在,如何实现Facade? 当然,您可以使用C#interface定义外观将向客户端公开的函数、事件、属性。实际的包装器将是一个实现此接口的class

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

我的另一个问题是,你能否将外观模式与观察者模式一起使用。外观类将观察所有主题,然后控制调用不同类中的方法,具体取决于主题?
从语义纯度的角度来看,外观应该只在包装子系统现有API的基础上定义新API。
如果您隐藏的系统需要发布者-订阅者模型,则需要在外观中实现观察者模式,包装子系统的发布者-订阅者调用。
但是,在简单性的名义下不要为外观添加更多职责或逻辑,将其视为子系统的相当简单的倡导者或促进者,不要成为无所不知的“上帝”对象。
这让我想到,使用外观并没有太多意义,因为外观的整个重点是为客户端提供便利(或隐藏信息),或者我错了?
在你的示例中,所有这些逻辑不应该放在外观中。我同意这会让客户更容易使用,但它并没有构建在包装的子系统之上,就像外观提供的附加服务一样。 因此,将其从外观中分离出来成为一个单独的类,客户端可以使用该类使其生活更轻松。 如果外观中有任何可以被拆分出来而不暴露子系统的东西,那么这个东西必须被拆分出来。 希望对你有所帮助。如果有任何不清楚的地方或者你认为我漏掉了你的任何问题,请给我留言,我们可以从那里开始。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接