写给自己看的小设计7 - 对象设计过程之对象交互

  对象创建完了以后,就是互相协作完成系统的功能。对象的协作方式通常有如下方式:
 
直接引用,互通有无
  这种方式最为自然,最为直接,最为简单,也是通常情况下的首选。不管是传参数,还是直接创建后直接使用对象的方法,都是属于这种情况:
public class ComponentB
{
 public void Run(ComponentA componentA) { componentA.Say(); }
}

 

依靠中介通信
  当对象之间的交互复杂起来以后,直接的通信可能耦合度就太高了,这个时候要靠辅助对象来间接的传递信息,很多的设计模式其实都是该思想的衍生物,比如中介者模式,代理模式,外观模式等。
  这里看一个UI层的例子:程序的界面上有3个对象,分别是Panel,Timeline,Scrollbar;这些对象之间分属不同的层次结构,但是它们之间需要互相操作,比如Scrollbar动的时候,需要修改Timeline上的一些小控件,而且需要操作Panel的一些控件,操作Timeline上的内容的时候,需要检查是否需要滚动Scrollbar,是否需要操作Panel,总之就是这3个对象的其中一个动的话,另外两个都要有些操作需要进行。
  针对这个交互的问题,我大概有3种想法:
第一种:3个对象之间互相引用,当需要的时候就直接调用对方的方法。
  比如Panel类的伪代码就可能如下所示:
class Panel
{
  private Timeline m_timeline;
  private Scrollbar m_scrollbar;
 
  public void Action()
  {
    DoSomething();
    m_timeline.M1();
    m_scrollbar.M2();
  }  
}

 

第二种:能否尝试使用事件来回调自己的方法。
  在脑海中就是那么一想,我决定放弃这个想法。
 
第三种:创建一个中介对象,所有交互的代码都放到这个中介中。
  中介者和Panel的伪代码如下:
class Mediator
{
  private Timeline m_timeline;
  private Scrollbar m_scrollbar;
  private Panel m_panel;
 
  public void Initialize(Timeline timeline, Scrollbar scrollbar, Panel panel)
  {
    m_timeline = timeline;
    m_scrollbar = scrollbar;
    m_panel = panel;
  }
 
  public void PanelAction()
  {
    m_timeline.M1();
    m_scrollbar.M2();
  }
 
  public void ScrollbarAction()
  {
    m_timeline.M1();
    m_panel.M3();
  }
 
  public void TimelineAction()
  {
    m_scrollbar.M2();
    m_panel.M3();
  }
}
 
class Panel
{
  Mediator m_mediator;
  public void Action()
  {
    DoSomething();
    m_mediator.PanelAction();
  }
}

如果说前两种实现是形成了3个对象之间的网状图的话,那么第三种实现其实是多加了中介者对象,4个对象形成了以中介者为中心的星状图。

  我实际采用了第三种实现来简化对象之间的交互关系。我这里并不想说第三种一定比前两种好,如果没有变化存在,只要是让系统正常工作的代码其实都是好代码。我总是认为,并不是任何时候状态模式就比分支语句更合适一个对象

  其实除了依靠进程内对象通信外,依靠程序外的媒介通信也属于这种情况,比如利用共享内存,Socket,文件系统中的文件,第三方消息队列(如MSMQ),分布式中间件等通信。
 
 
使用事件通信
  当对象之间通信方式同质化严重(工作方式基本类似,交互的方式也类似),特别是1对多的通信,而且交互对象数量较多,类型未知时,事件,或者说是观察者模式,或者说是发布订阅模式就可以使用起来了。
  让我再来回忆一下前面使用过的一个例子:
public class Program
{
 static void Main(string[] args)
 {
  User user = new User();
  View ui = new View(user);
  user.Name = "Hello";
 }
}
 
delegate void OnNameChange(string name);
class View
{
 public View(User user)
 {
  user.onNameChanged += user_onNameChanged;
 }
 
 void user_onNameChanged(string name)
 {
  Console.WriteLine(name);
 }
}
 
class User
{
 private string m_name;
 public string Name
 {
  get { return m_name; }
  set
  {
   m_name = value;
   onNameChanged(m_name);
  }
 }
 public event OnNameChange onNameChanged;
}

 

交互即耦合
  其实对象交互的方式,本质上就是描述了对象耦合的方式。强耦合的关系一旦面临变化,一般会引起较大范围的改动。众多模式的出现都是为了给对象解耦。
  对象之间的交互产生了对象之间的耦合,这是自然而有效的,对象之间就是要通过交互而形成流程,进而完成整个业务。除了上面这些描述的耦合方式,其实还有一个耦合方式更加紧密,那就是继承。
  看一段简单的代码:

class Base {}
class Derived : Base {}

这样简单的代码,其实就是形成了一个相当紧密的耦合:Base的任何修改都会毫无保留的传递给Derived。

  同时,基于这样的一个分析,我们可以解释一个常识:为什么基类要尽量的抽象,不完成特别具体的细节。这么做就是为了尽量维护基类的稳定,让基类尽量不变化

  总结来说,对象交互的常用方式就是:"继承+组合(直接引用)+中介+事件",而这些方式的耦合级别也是基本按这个顺序越来越弱

 
 
 
posted @ 2015-06-02 12:34  沙场秋点兵  阅读(1040)  评论(1编辑  收藏  举报