我目前正在开发两种在功能上非常相似的形式,虽然一种形式用于PC,另一种形式适合在低分辨率扫描仪设备上使用。
我想尽可能多地分享逻辑,这就是我为此使用MVP(被动视图)的原因。
但是扫描仪版本与它有一些细微差别,例如动态显示和隐藏一些控件。这当然是我想要放入Presenter的东西,所以我可以对它进行单元测试。
所以我的问题是:我应该把这个逻辑放在同一个演示者身上吗?或者我应该通过继承来演变这个演示者的变体吗?或者我应该简单地将所有内容分开,专用于特定表单,尽管我的视图和模型对于两种表单都完全相同?
答案 0 :(得分:3)
所以我的问题是:我应该把这个逻辑放在同一个演示者身上吗?
在我看来,没有。演示者有不同的职责,因此逻辑应该分成专用的演示者类。
我应该通过继承来演绎这位演示者的变体吗?
在我看来,继承是解决这个问题的错误方法。演示者唯一的共同点就是他们碰巧在相同的视图和模型对上行动。
因此,您需要将演示者功能移动到单独的类中,但不使用继承。之前我曾说过,演示者之间唯一的共同点是它们对相同的视图和模型对起作用。考虑到这一点,我们可以为演示者创建以下抽象:
public interface Presenter<M, V>
{
M Model { get; }
V View { get; }
}
您可以实现此接口以创建具体的演示者,该演示者为演示者子类提供所需的视图和模型对。接下来,为PC和Scanner演示者创建单独的演示者类,它们提供每个视图所需的功能。
您的想法是,现在可以使用decorator pattern根据当前视图构建所需的演示者功能。为此,您可以创建以下类:
public abstract class PresenterDecorator<M, V> : Presenter<M, V>
{
private readonly Presenter<M, V> decoratedPresenter;
public PresenterDecorator(Presenter<M, V> decoratee)
{
this.decoratedPresenter = decoratee;
}
public M Model
{
get { return this.decoratedPresenter.Model; }
}
public V View
{
get { return this.decoratedPresenter.View; }
}
}
这允许您包装现有的演示者并使用其他功能对其进行扩充,前提是它们可以在相同的视图和模型对上进行操作。
要使用所有这些,首先需要一个提供相关视图和模型类型的具体演示者。理想情况下,视图和模型将注入到构造函数中,这意味着此演示者除了从Presenter
接口的实现提供的属性返回视图和模型之外,不应该执行任何操作。理想情况下,模型应注入已设置的默认值。请注意,此功能可以移动到通用基类中,允许您初始化任何视图/模型对。
接下来,对于视图/模型对的每个离散功能,您必须创建一个派生自PresenterDecorator
类的演示者。在您的情况下,您将拥有DefaultPresenter
和ScannerPresenter
。这些演示者类中的每一个还可以提供自定义构造函数,除了要装饰的演示者之外,还提供任何所需的服务。
在应用程序中负责打开PC屏幕并配置演示者的部分,您可以执行以下操作:
var model = ...
var view = ...
var presenter = new DefaultPresenter(new PresenterInitializer(view, model));
并且在您的应用程序中负责打开扫描仪屏幕并配置演示者的部分,您将执行以下操作:
var model = ...
var view = ...
var presenter =
new ScannerPresenter(new DefaultPresenter(PresenterInitializer(view, model)));
请注意,在配置扫描仪屏幕时,默认的演示者将使用ScannerPresenter
类进行修饰,该类提供您需要的其他功能(为了最大限度地提高灵活性,应编写每个演示者装饰器,以便构建顺序无关紧要)
依赖注入框架可用于简化应用程序的连接和配置,以便在需求发生变化时不需要手动重写上述代码。
现在看起来需要做很多工作才能启动和运行,但是一旦核心抽象到位,它就可以很容易地为您的应用程序添加新功能。
如果给出了新的要求,您只需创建一个新的presenter类,该类派生自PresenterDecorator
类并更改您的配置代码以适应(提示:使用依赖注入框架使这更容易)。请注意,您不必触及任何现有的演示者类。每个演示者类也可以完全隔离测试。
您还可以创建通用的presenter装饰器,它可以应用于任何视图和模型对,这使得数据验证非常简单。
我理解这是一个很长的答案,需要考虑很多,所以如果您需要进一步澄清或对使用这种方法有任何想法,请发表评论。