假设我正在为硬件配件编写用户界面。这个配件有两个版本 - 比方说Widget Lite和Widget Pro。
Widget Pro可以完成Widget Lite可以执行的所有操作,但是可以使用更多选项,并且可以执行Widget Lite无法完成的一些操作。更详细的说,Widget Lite有一个通道,widget Pro有两个,所以当涉及类似于音量控制的东西时,我只需要一个Lite控件,但是Pro只允许独立控制两个。
在我第一次尝试构建一个应用程序来处理这个问题时,我有一个代表Widget Pro扩展Widget Lite的类,但后来我最终得到了各种条件案例来处理看起来很难看的差异。有谁知道合适的设计模式来帮助解决这种情况?我的想象力在提出可能有助于我搜索的同义情境时留下了空白。
答案 0 :(得分:2)
我首先看一下plug in模式(依赖倒置的一种形式)。
尝试并抽象Lite和Pro版本常用的界面,例如
interface IBaseWidget
{
IControl CreateVolumeControl();
// ... etc
}
在单独的程序集/ dll中,实现Lite和Pro小部件:
class LiteWidget : IBaseWidget
{
int _countCreated = 0;
IControl CreateVolumeControl()
{
_countCreated++;
if (_countCreated > 1)
{
throw new PleaseBuyTheProVersionException();
}
}
}
由于您不希望使用Lite部署分发Pro版本,因此您需要在运行时加载dll,例如按惯例(例如,您的基本应用程序查找DLL的名称为* Widget.dll),或者通过Configuration查找IBaseWidget
的适用具体实现。根据@ Bartek的评论,理想情况下,您不希望您的基本引擎类能够“了解”IBaseWidget
的特定具体类。
答案 1 :(得分:1)
访客模式可能对您有用。检查dofactory。
访客班......
...声明对每个ConcreteElement类的一个Visit操作 对象结构。操作的名称和签名标识了 将访问请求发送给访问者的类。这让我们 访问者确定被访问元素的具体类。 然后访问者可以通过它直接访问元素 特定界面
这类似于Vikdor所说的抽象类实现。
Here是一个wiki链接。
访问者模式需要支持的编程语言 单一调度和方法重载。
我提供了一个非常简单的实现,使用访问者模式来满足您对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);
}
}
}
答案 2 :(得分:0)
我强烈建议你有Widget
和Widget Lite
来自的基础Widget Pro
课程。
public class Widget {}
public class WidgetLite : Widget {}
public class WidgetPro : Widget {}
拥有基类中Pro
和Lite
共享的属性/方法。这对你来说是一个更清洁的设计。
答案 3 :(得分:0)
我会这样做:
AbstractWidget (Abstract class)
/\
/ \
/ \
/ \
/ \
WidgetLite WidgetPro
公共代码将进入AbstractWidget(抽象,因为它不应该被实例化),并且这两个类之间不同的行为将进入具体类。