2012-08-29 15 views
1

假设我正在编写硬件附件的用户界面。有两种版本的配件 - 比如Widget Lite和Widget Pro。具有可扩展功能的用户界面的设计模式

Widget Pro可以完成Widget Lite可以做的所有事情,但是还有其他几个选项可以做Widget Lite不能做的一些事情。更详细地说,Widget Lite有一个通道,Pro有两个通道,所以当涉及类似于音量控制的事情时,我只需要Lite的一个控件,但是两个Pro允许独立控制。

在我第一次尝试构建一个应用程序来处理这个问题时,我有了代表Widget Pro扩展Widget Lite的类,但后来我用各种有条件的情况来处理看起来很丑的区别。有没有人知道一个合适的设计模式来帮助这种情况?在想出可能有助于我的搜索的同义词的情况下,我的想象力正在空白。

+1

在您的示例中,LSP违规是至关重要的。如果您可以找到Lite不能用Pro取代的地方,则无法从Lite中派生Pro。 –

回答

0

如下我会做到这一点:

   AbstractWidget (Abstract class) 
        /\ 
       /\ 
       / \ 
       / \ 
      /  \ 
     WidgetLite WidgetPro 

通用代码将进入AbstractWidget(抽象的,因为,它不应该被实例化),这是这两个类将进入之间的不同行为具体的类。

0

我强烈建议你有一个Widget类,其中Widget LiteWidget Pro派生自。

public class Widget {} 

public class WidgetLite : Widget {} 

public class WidgetPro : Widget {} 

具有由两个ProLite基类内共享的属性/方法。这对你来说是一个更清洁的设计。

2

我会先看看plug in模式(依赖反转的一种形式)。

尝试并抽象Lite和Pro版本通用的接口,例如

interface IBaseWidget 
{ 
    IControl CreateVolumeControl(); 
    // ... etc 
} 

在单独的组件/ DLL文件,实现您的精简版和专业版小工具:

class LiteWidget : IBaseWidget 
{ 
    int _countCreated = 0; 
    IControl CreateVolumeControl() 
    { 
     _countCreated++; 
     if (_countCreated > 1) 
     { 
      throw new PleaseBuyTheProVersionException(); 
     } 
    } 
} 

既然你不想分配与精简版部署Pro版本,您将需要加载动态链接库在运行时,例如按照惯例(例如,您的基本应用程序查找DLL的名为* Widget.dll),或者通过配置找到适用的IBaseWidget的具体实现。根据@ Bartek的评论,理想情况下,你不希望你的基础引擎classfactory“知道”IBaseWidget的具体类。

+0

为什么会违反LSP(classfactory contra实现)? – jgauffin

+0

@jgauffin认为这不是LSP的技术违规 – StuartLC

1

访客模式可能对您有用。检查dofactory

访问者类...

...声明一个访问操作在 对象结构中的每个类ConcreteElement的。该操作的名称和签名标识将访问请求发送给访问者的类 。这让 访问者确定正在访问的元素的具体类。 然后,游客可以直接通过其 特定接口

这类似于抽象类实现访问的元素通过Vikdor所讲述。

Here是此链接的维基链接。

访问者模式需要一种支持 单一调度和方法重载的编程语言。

我已经提供了一个非常简单的实现,使用访客模式来满足您对WidgetLite和Pro的不同频道和音量设置的需求。我在评论中提到访问者模式将大大帮助您减少if-else调用。

基本的理念是您将控件(例如音量)传递给小部件,并且会根据需要知道如何使用它。因此,控制对象本身具有非常简化的实现。每个部件的代码保持在一起!

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace WidgetVisitor 
{ 
    //This is the widget interface. It ensures that each widget type 
    //implements a visit functionality for each control. The visit function 
    //is overloaded here. 
    //The appropriate method is called by checking the parameter and this 
    //avoids the if-then logic elegantly 
    public interface Widget 
    { 
     void visit(Volume vol); 
     void visit(Channel chan); 
     void Display(AllControls ac); 
    } 

    //This is the interface which defines the controls. Each control that 
    //inherits this interface needs to define an "accept" method which 
    //calls the appropriate visit function of the right visitor, 
    //with the right control parameter passed through its call! 
    //This is how the double dispatch works. 
    //Double dispatch: A mechanism that dispatches a function call to different concrete 
    //functions depending on the runtime types of two objects involved in the call. 
    public interface WidgetControls 
    { 
     void accept(Widget visitor); 

    } 

    //I have implemented the volume control and channel control 
    //Notice how the code for defining each control is the SAME 
    //in visitor pattern! This is double dispatch in action 
    public class Volume : WidgetControls 
    { 
     public int volLevel { get; set; } 
     public int volJazz { get; set; } 
     public int volPop { get; set; } 
     public void accept(Widget wc) 
     { 
      wc.visit(this); 
     } 
    } 

    public class Channel : WidgetControls 
    { 
     public int channelsProvided { get; set; } 
     public int premiumChannels { get; set; } 
     public void accept(Widget wc) 
     { 
      wc.visit(this); 
     } 
    } 

    //Widget lite implementation. Notice the accept control implementation 
    //in lite and pro. 
    //Display function is an illustration on an entry point which calls the 
    //other visit functions. This can be replaced by any suitable function(s) 
    //of your choice 
    public class WidgetLite : Widget 
    { 
     public void visit(Volume vol) 
     { 
      Console.WriteLine("Widget Lite: volume level " + vol.volLevel); 
     } 

     public void visit(Channel cha) 
     { 
      Console.WriteLine("Widget Lite: Channels provided " + cha.channelsProvided); 
     } 

     public void Display(AllControls ac) 
     { 
      foreach (var control in ac.controls) 
      { 
       control.accept(this); 
      } 

      Console.ReadKey(true); 
     } 
    } 

    //Widget pro implementation 
    public class WidgetPro : Widget 
    { 
     public void visit(Volume vol) 
     { 
      Console.WriteLine("Widget Pro: rock volume " + vol.volLevel); 
      Console.WriteLine("Widget Pro: jazz volume " + vol.volJazz); 
      Console.WriteLine("Widget Pro: jazz volume " + vol.volPop); 
     } 

     public void visit(Channel cha) 
     { 
      Console.WriteLine("Widget Pro: Channels provided " + cha.channelsProvided); 
      Console.WriteLine("Widget Pro: Premium Channels provided " + cha.premiumChannels); 
     } 

     public void Display(AllControls ac) 
     { 
      foreach (var control in ac.controls) 
      { 
       control.accept(this); 
      } 

      Console.ReadKey(true); 
     } 
    } 

    //This is a public class that holds and defines all the 
    //controls you want to define or operate on for your widgets 
    public class AllControls 
    { 
     public WidgetControls [] controls { get; set; } 

     public AllControls(int volTot, int volJazz, int volPop, int channels, int chanPrem) 
     { 
      controls = new WidgetControls [] 
      { 
       new Volume{volLevel = volTot, volJazz = volJazz, volPop = volPop}, 
       new Channel{channelsProvided = channels, premiumChannels = chanPrem} 
      }; 
     } 
    } 

    //finally, main function call 
    public class Program 
    { 
     static void Main(string[] args) 
     { 
      AllControls centralControl = new AllControls(3, 4, 2, 5, 10); 

      WidgetLite wl = new WidgetLite(); 
      WidgetPro wp = new WidgetPro(); 

      wl.Display(centralControl); 
      wp.Display(centralControl); 

     } 
    } 
} 
+0

-1你应该解释为什么它会帮助他。举例说明,而不是引用任意模式。 (如果你解决了这个问题,我会赞成的) – jgauffin

+0

@jgauffin,我会尽快更新它适合的解释! – TheSilverBullet

+0

@jgauffin,这里是一个粗略的解释。我指的是我在初始答案中提供的维基链接。我也提到[this](http://stackoverflow.com/questions/2604169/could-someone-in-simple-terms-explain-to-me-the-visitor-patterns-purpose-with-e/2604294 #2604294)写得非常好的解释。请检查这个问题的第二个答案。在这里,'WidgetLite'和'WidgetPro'是实现'visit'功能的两个访客类。 “接受”这些访问者的音量和频道等控件。 – TheSilverBullet