I have a s/w design question. Say I have a windows form with some elements and I have a customer object. A customer can either be business, private or corporate for example.
我有一个s / w设计问题。假设我有一些带有一些元素的窗体,我有一个客户对象。例如,客户可以是商业,私人或公司。
Now, all the decisions of what is going to happen in the form will depend on the customer type. For instance, certain elements will be hidden, certain label text will be different, etc...events will respond differently.
现在,表格中将要发生的所有决定将取决于客户类型。例如,某些元素将被隐藏,某些标签文本将不同等等......事件将以不同方式响应。
Obviously, one way to code this will be to use CASE statement each time the decision needs to be made. Another way would be to have a Customer class and 3 other classed such as BusinessCustomer, PrivateCustomer and CorporateCustomer inherit from the base class. In the latter, a question then arises: how are you going to incorporate a windows from into it....
显然,对此进行编码的一种方法是每次需要做出决定时使用CASE语句。另一种方法是拥有一个Customer类,另外三个类,例如BusinessCustomer,PrivateCustomer和CorporateCustomer继承自基类。在后者中,出现了一个问题:你如何将窗户纳入其中......
Please share your elegance, much thanks in advance.
请分享您的优雅,非常感谢提前。
EDITED: I just has an idea: can i embed forms within forms? My reqs don't dictate two windows being shown at once, so I dont have to use MDI. But to simplify my design based on some ppl's comments here, i would like to maintain 3 different customer forms, and embed inside the main form on the fly. That way three GUIs are separated and I won't have to deal with every control's visibility.
编辑:我只是有一个想法:我可以在表格中嵌入表格吗?我的reqs并没有规定同时显示两个窗口,所以我不必使用MDI。但是为了简化我的设计,基于这里的一些ppl的评论,我想维护3种不同的客户表单,并在运行中嵌入主表单。这样三个GUI就分开了,我不必处理每个控件的可见性。
Now, anyone here had any experience with this? I am assuming I can just add a form to another form, such as this:
现在,这里的任何人都有过这方面的经验吗?我假设我可以将表单添加到另一个表单,例如:
Form child_form = new Form();
parent_form.Controls.Add(child_form);
6 个解决方案
#1
Those decisions really shouldn't be made in the GUI. You should have a ViewModel behind your GUI that makes those decisions, and that you write unit tests for. (Or a Presenter, or a Controller -- different names that all mean roughly the same thing: get the decisions out of the GUI class and into something you can unit test.)
这些决定实际上不应该在GUI中做出。您应该在GUI后面有一个ViewModel来做出这些决定,并为你编写单元测试。 (或者是一个演示者,或者一个控制器 - 所有不同的名字都意味着大致相同的事情:从GUI类中获取决策并进入可以单元测试的东西。)
Then your ViewModel would have e.g. a Boolean property for each element that the GUI would disable, and a method for each action you could take (CloseCustomerAccount() or whatever).
然后你的ViewModel会有例如GUI将禁用的每个元素的布尔属性,以及您可以采取的每个操作的方法(CloseCustomerAccount()或其他)。
As long as the Form is created for a particular type of customer, and the customer won't change to a different type of customer during the lifetime of the form, you could just pass your Customer object (that stores all of the actual customer data) to the ViewModel's constructor, and then pass your ViewModel to your Form's constructor. The form could set all its Enabled properties right after it calls InitializeComponent(). On the other hand, if the customer type could change, then your ViewModel needs to expose some events for the Form to hook, so the form knows when to re-run its Enabling logic.
只要为特定类型的客户创建表单,并且客户在表单生命周期内不会更改为其他类型的客户,您就可以传递Customer对象(存储所有实际客户数据) )到ViewModel的构造函数,然后将ViewModel传递给Form的构造函数。表单可以在调用InitializeComponent()之后立即设置其所有Enabled属性。另一方面,如果客户类型可能发生变化,那么您的ViewModel需要为Form挂起一些事件,因此表单知道何时重新运行其启用逻辑。
Your question then moves out of the Form and into the ViewModel. Do you have one ViewModel class with a bunch of case statements, or three ViewModel classes (maybe with a fourth that's a base class) that use polymorphism, and a factory method somewhere that decides, based on the particular customer, which ViewModel class to instantiate?
然后您的问题移出Form并进入ViewModel。你有一个带有一堆case语句的ViewModel类,或者有三个使用多态的ViewModel类(可能有第四个是基类),还有一个工厂方法根据特定客户决定实例化哪个ViewModel类?
I'd let your code be the guide there. Start with the simplest approach, which is probably case statements. Write a unit test for every behavior you care about. If the case statements start to get too awkward, then add a ViewModel descendant for each customer type, and start extracting the case statements into virtual methods. Your tests will catch you if you make a mistake during the refactor.
我会让你的代码成为那里的指南。从最简单的方法开始,这可能是案例陈述。为您关心的每个行为编写单元测试。如果case语句开始变得太尴尬,那么为每个客户类型添加一个ViewModel后代,并开始将case语句提取到虚方法中。如果你在重构期间犯了错误,你的测试会抓住你。
#2
If you do have 3 different windows, each handling a specific type of customer than there won't be much point in working with the base class or a contract. You could be smart though with a factory class that takes a customer class and determines the correct screen to use.
如果你有3个不同的窗口,每个窗口处理一个特定类型的客户,那么在使用基类或合同时没有多大意义。虽然工厂类需要客户类并确定要使用的正确屏幕,但您可能很聪明。
I've run into this quite a bit. I end up with a base window that handles the generic stuff and then extend it for each concrete type.
我已经遇到了这个问题。我最终得到一个基本窗口,处理通用的东西,然后为每个具体类型扩展它。
#3
I agree with Joshua Belden's answer. Three separate forms for different kinds of customers would likely be the easiest to maintain.
我同意Joshua Belden的回答。针对不同类型客户的三种单独形式可能是最容易维护的。
Also, in case you didn't know already, you can derive from a Form class and tweak it in a derived Form class. This is even supported by the designer.
此外,如果您还不知道,可以从Form类派生并在派生的Form类中调整它。这甚至得到了设计师的支持。
However, I'd like to offer an alternative:
但是,我想提供一个替代方案:
The Bridge Pattern: separate an abstraction from its implementation so the two can vary independently.
桥模式:将抽象与其实现分开,以便两者可以独立变化。
What you could do is this:
你能做的是:
Create three separate UIImplementation
classes. These classes could tweak the UI, and the events for the Customer
form. In order to gain access to the private members of the form, you would need to declare the UIImplementation
classes as nested within the CustomerForm
class. (Use partial classes to separate these into different files). If the form itself is significant, and the tweaks are insignificant, this may be good option. It's difficult to say.
创建三个单独的UIImplementation类。这些类可以调整UI以及Customer表单的事件。为了获得对表单私有成员的访问权限,您需要将UIImplementation类声明为嵌套在CustomerForm类中。 (使用部分类将这些分成不同的文件)。如果表单本身很重要,并且调整不重要,这可能是一个不错的选择。这很难说。
#4
Just go for the three classes implementing some abstract Customer interface. In your app you would have a variable customer of type Customer and an object of a particular Customer type would be stored in there. Your GUI could then just rely on the interface and invoke methods on the customer variable regardless of what customer would be actually interacting with the GUI.
只需要实现一些抽象的Customer接口的三个类。在您的应用程序中,您将拥有Customer类型的可变客户,并且特定客户类型的对象将存储在那里。然后,您的GUI可以仅依赖于接口并调用客户变量上的方法,而不管客户实际与GUI交互的是什么。
Have a look at this article.
看看这篇文章。
#5
I would go with an MVP type approach and define CustomerPresenter class that exposes a boolean property that will derive your UI controls' enable/disable state via binding.
我将使用MVP类型方法并定义CustomerPresenter类,该类公开一个布尔属性,该属性将通过绑定派生您的UI控件的启用/禁用状态。
public class CustomerPresenter
{
private Customer _customer;
public CustomerPresenter(Customer customer)
{
_customer = customer;
}
public bool EnableUI
{
get
{
//TODO: put your customer type basic logic here (switch statement or if/else)
return _customer.Type == CustomerType.Business;
}
}
}
public CustomerForm: WinForm
{
private CustomerPresenter _customerPresenter;
public CustomerForm(){};
public CustomerForm(Customer customer)
{
_customerPresenter = new CustomerPresenter(customer);
}
private void CustomerForm_Load(object sender, EventArgs e)
{
_someButton.DataBindings.Add("Enabled",_customerPresenter,"EnableUI");
}
}
#6
The solution should be driven by data, not your code.
解决方案应该由数据驱动,而不是代码。
So if you tag various controls as being visible in state "A", "B", or "C", then a single method could look at its state and know which controls to make visible or not.
因此,如果您将各种控件标记为在状态“A”,“B”或“C”中可见,则单个方法可以查看其状态并知道哪些控件可见或不可见。
You know you did it right when adding new controls for state "D" takes no code changes aside from those required to add the controls themselves.
你知道你在为状态“D”添加新控件时没有做任何代码更改,除了添加控件本身所需的代码之外。
You might also look at breaking the form down into 4 sub-forms, shared, sub-form a, sub-b, then displaying both "Shared" and "Sub-a" together on a parent form ...
您还可以查看将表单分解为4个子表单,共享,子表单a,sub-b,然后在父表单上同时显示“Shared”和“Sub-a”...
#1
Those decisions really shouldn't be made in the GUI. You should have a ViewModel behind your GUI that makes those decisions, and that you write unit tests for. (Or a Presenter, or a Controller -- different names that all mean roughly the same thing: get the decisions out of the GUI class and into something you can unit test.)
这些决定实际上不应该在GUI中做出。您应该在GUI后面有一个ViewModel来做出这些决定,并为你编写单元测试。 (或者是一个演示者,或者一个控制器 - 所有不同的名字都意味着大致相同的事情:从GUI类中获取决策并进入可以单元测试的东西。)
Then your ViewModel would have e.g. a Boolean property for each element that the GUI would disable, and a method for each action you could take (CloseCustomerAccount() or whatever).
然后你的ViewModel会有例如GUI将禁用的每个元素的布尔属性,以及您可以采取的每个操作的方法(CloseCustomerAccount()或其他)。
As long as the Form is created for a particular type of customer, and the customer won't change to a different type of customer during the lifetime of the form, you could just pass your Customer object (that stores all of the actual customer data) to the ViewModel's constructor, and then pass your ViewModel to your Form's constructor. The form could set all its Enabled properties right after it calls InitializeComponent(). On the other hand, if the customer type could change, then your ViewModel needs to expose some events for the Form to hook, so the form knows when to re-run its Enabling logic.
只要为特定类型的客户创建表单,并且客户在表单生命周期内不会更改为其他类型的客户,您就可以传递Customer对象(存储所有实际客户数据) )到ViewModel的构造函数,然后将ViewModel传递给Form的构造函数。表单可以在调用InitializeComponent()之后立即设置其所有Enabled属性。另一方面,如果客户类型可能发生变化,那么您的ViewModel需要为Form挂起一些事件,因此表单知道何时重新运行其启用逻辑。
Your question then moves out of the Form and into the ViewModel. Do you have one ViewModel class with a bunch of case statements, or three ViewModel classes (maybe with a fourth that's a base class) that use polymorphism, and a factory method somewhere that decides, based on the particular customer, which ViewModel class to instantiate?
然后您的问题移出Form并进入ViewModel。你有一个带有一堆case语句的ViewModel类,或者有三个使用多态的ViewModel类(可能有第四个是基类),还有一个工厂方法根据特定客户决定实例化哪个ViewModel类?
I'd let your code be the guide there. Start with the simplest approach, which is probably case statements. Write a unit test for every behavior you care about. If the case statements start to get too awkward, then add a ViewModel descendant for each customer type, and start extracting the case statements into virtual methods. Your tests will catch you if you make a mistake during the refactor.
我会让你的代码成为那里的指南。从最简单的方法开始,这可能是案例陈述。为您关心的每个行为编写单元测试。如果case语句开始变得太尴尬,那么为每个客户类型添加一个ViewModel后代,并开始将case语句提取到虚方法中。如果你在重构期间犯了错误,你的测试会抓住你。
#2
If you do have 3 different windows, each handling a specific type of customer than there won't be much point in working with the base class or a contract. You could be smart though with a factory class that takes a customer class and determines the correct screen to use.
如果你有3个不同的窗口,每个窗口处理一个特定类型的客户,那么在使用基类或合同时没有多大意义。虽然工厂类需要客户类并确定要使用的正确屏幕,但您可能很聪明。
I've run into this quite a bit. I end up with a base window that handles the generic stuff and then extend it for each concrete type.
我已经遇到了这个问题。我最终得到一个基本窗口,处理通用的东西,然后为每个具体类型扩展它。
#3
I agree with Joshua Belden's answer. Three separate forms for different kinds of customers would likely be the easiest to maintain.
我同意Joshua Belden的回答。针对不同类型客户的三种单独形式可能是最容易维护的。
Also, in case you didn't know already, you can derive from a Form class and tweak it in a derived Form class. This is even supported by the designer.
此外,如果您还不知道,可以从Form类派生并在派生的Form类中调整它。这甚至得到了设计师的支持。
However, I'd like to offer an alternative:
但是,我想提供一个替代方案:
The Bridge Pattern: separate an abstraction from its implementation so the two can vary independently.
桥模式:将抽象与其实现分开,以便两者可以独立变化。
What you could do is this:
你能做的是:
Create three separate UIImplementation
classes. These classes could tweak the UI, and the events for the Customer
form. In order to gain access to the private members of the form, you would need to declare the UIImplementation
classes as nested within the CustomerForm
class. (Use partial classes to separate these into different files). If the form itself is significant, and the tweaks are insignificant, this may be good option. It's difficult to say.
创建三个单独的UIImplementation类。这些类可以调整UI以及Customer表单的事件。为了获得对表单私有成员的访问权限,您需要将UIImplementation类声明为嵌套在CustomerForm类中。 (使用部分类将这些分成不同的文件)。如果表单本身很重要,并且调整不重要,这可能是一个不错的选择。这很难说。
#4
Just go for the three classes implementing some abstract Customer interface. In your app you would have a variable customer of type Customer and an object of a particular Customer type would be stored in there. Your GUI could then just rely on the interface and invoke methods on the customer variable regardless of what customer would be actually interacting with the GUI.
只需要实现一些抽象的Customer接口的三个类。在您的应用程序中,您将拥有Customer类型的可变客户,并且特定客户类型的对象将存储在那里。然后,您的GUI可以仅依赖于接口并调用客户变量上的方法,而不管客户实际与GUI交互的是什么。
Have a look at this article.
看看这篇文章。
#5
I would go with an MVP type approach and define CustomerPresenter class that exposes a boolean property that will derive your UI controls' enable/disable state via binding.
我将使用MVP类型方法并定义CustomerPresenter类,该类公开一个布尔属性,该属性将通过绑定派生您的UI控件的启用/禁用状态。
public class CustomerPresenter
{
private Customer _customer;
public CustomerPresenter(Customer customer)
{
_customer = customer;
}
public bool EnableUI
{
get
{
//TODO: put your customer type basic logic here (switch statement or if/else)
return _customer.Type == CustomerType.Business;
}
}
}
public CustomerForm: WinForm
{
private CustomerPresenter _customerPresenter;
public CustomerForm(){};
public CustomerForm(Customer customer)
{
_customerPresenter = new CustomerPresenter(customer);
}
private void CustomerForm_Load(object sender, EventArgs e)
{
_someButton.DataBindings.Add("Enabled",_customerPresenter,"EnableUI");
}
}
#6
The solution should be driven by data, not your code.
解决方案应该由数据驱动,而不是代码。
So if you tag various controls as being visible in state "A", "B", or "C", then a single method could look at its state and know which controls to make visible or not.
因此,如果您将各种控件标记为在状态“A”,“B”或“C”中可见,则单个方法可以查看其状态并知道哪些控件可见或不可见。
You know you did it right when adding new controls for state "D" takes no code changes aside from those required to add the controls themselves.
你知道你在为状态“D”添加新控件时没有做任何代码更改,除了添加控件本身所需的代码之外。
You might also look at breaking the form down into 4 sub-forms, shared, sub-form a, sub-b, then displaying both "Shared" and "Sub-a" together on a parent form ...
您还可以查看将表单分解为4个子表单,共享,子表单a,sub-b,然后在父表单上同时显示“Shared”和“Sub-a”...