开/闭原理-如何处理这个开关?
我一直在研究开放封闭原则,听起来不错,所以我想实践它的教义。 我考虑将新发现的知识应用到现有项目中,但很快就陷入了困境。
如果出现新的 UserType(这很可能),则需要更改它,但尚未关闭修改。怎么能绕过这个呢?
从我读到的内容来看,听起来我应该在这里实现一个工厂而不是应用 OCP?
private void BuildUserTree(User user)
{
switch (user.UserType)
{
case UserType.FreeLoader:
BuildFreeLoaderTree();
break;
case UserType.Premium:
BuildPremiumTree();
break;
case UserType.Unlimited:
BuildUnlimitedTree();
break;
default:
throw new Exception("No UserType set");
}
}
谢谢, 科汉
I have been looking into the open closed principle and it sounds good and so I wanted to exercise it's teachings.
I looked at applying my new found knowledge to an existing project and have become a little stuck right away.
If a new UserType comes along (And this is Very likely), this will need to be changed, it is not yet closed to modification. How could one get round this?
From what I have read, it sounds like I should be implementing a factory here instead of applying the OCP?
Factory which breaks Open-closed principle
private void BuildUserTree(User user)
{
switch (user.UserType)
{
case UserType.FreeLoader:
BuildFreeLoaderTree();
break;
case UserType.Premium:
BuildPremiumTree();
break;
case UserType.Unlimited:
BuildUnlimitedTree();
break;
default:
throw new Exception("No UserType set");
}
}
Thanks,
Kohan
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
与任何“原则”一样,OCP 并不是您在所有情况下都必须遵守的规则。
我们被告知“优先选择组合而不是继承”,然而像装饰器和复合这样的模式公开地促进了继承。
类似地,我们被告知“对接口进行编程,而不是实现,但是,在我们的应用程序中的某个时刻,我们将不得不实例化某种描述的具体对象。”
您的解决方案是经典的工厂惯用法(如果不是完整的工厂方法或抽象工厂模式)。这就是它的目的。尝试对其应用 OCP 是没有意义的。
事实上,通过创建此方法,您实际上可以在代码库的其他部分中促进 OCP。既然您已经分离了创建,那么应用程序中的一些其他类现在可以遵守 OCP。
Like any 'principle' OCP is not a rule that you must obey on all occasions.
We're told to 'Favour composition over inheritance', and yet patterns like decorator and composite openly promote inheritance.
Similarly, we are told to 'Program to an interface, not an implemenation, and yet, at some point in our application we're going to have to instantiate a concrete object of some description.
Your solution is a classic factory idiom (if not quite the full factory method or abstract factory pattern). This is what it is intended to do. Trying to apply OCP to it doesn't make sense.
In fact, by creating this method, you may actually facilitate OCP in some other part of your code-base. Some other class or classes in your app could now obey OCP now you have separated the creation.
我会做如下:
然后,而不是每次添加新用户类型时都需要修改的方法和开关案例,只需调用:
然后,每当您需要添加新用户类型时,您都可以扩展代码而不是修改。您只需为新用户类型添加一个新类,而无需触及以前的类。
这就是他们所说的开放封闭,当你可以设法处理它时,为什么要违反它呢?
I would do as below:
then instead of a method and a switch case that needs to be modified every time you add a new user type and simply call:
then this way whenever you need to add a new user type you extend your code instead of modifying. you just add a new class for the new user type with no touch to previous classes.
thats what they call open closed and when you can manage to handle it why should you violate it ?
可用构建器列表可以使用 MEF MEF,从 Codeplex 迁移
The list of available builders could be build using MEF MEF, migrated from Codeplex
要消除类型切换,您必须将职责移回需要类型特定操作的类型。这种类型(在您的情况下为“用户”)拥有有关其自身的所有信息,并且可以根据这些知识轻松调用正确的操作。而且你必须利用继承。
在您的情况下,您必须通过简单继承或组合来反映用户类型。您的“User”将拥有一个属性“UserType”,就像在您的示例中一样,但它不是只是一个“Enum”之类的类型,而是成为一个继承“IUserType”接口并知道如何构造其特定类型的复杂类型依赖项(“UserType”实现“IUserType”)。 “IUserType”可以通过属性公开类型特定的属性(例如返回“ITypeSpecificTree”的“IUserType.TypeSpecificTree”)。
因此,当在您的示例中将“用户”升级为高级时,您只需将该属性设置为具体“IUserType”实现(例如 PremiumUserType”)的新实例,该实例执行其特定操作,例如构建高级树(“ITypeSpecificTree”) “实现)从您的示例以及构造关联类型。
这样,通过使用组合和继承消除了 switch 语句。我们将复杂的“UserType”属性转换为一个单独的类,然后将类型特定的职责移至类型本身。
继承,尤其是依赖倒置有助于在不知道具体类型的情况下对对象进行操作(例如获取用户类型特定信息,如(User.IUserType.IUserSpecificTree“)。这有助于确保我们开放扩展易于修改。
如果我们需要更改特定于类型的树的生成方式或该用户类型的行为方式,我们只需更改即可。触摸关联的“IUserType”如果添加了新的用户类型(扩展),则它们必须实现基本接口“IUserType”,并且不需要触及其他代码(例如 switch 语句)才能使其正常工作,并且不再需要任何类型。需要检查。
为了使其完整并提供更多的可扩展性,“User”类还应该实现一个接口,例如公开用户类型(例如“IUser.IUserType”)的“IUser”。
To eliminate the type switch you have to move responsibilities back to the type that requires a type specific action. This type, in your case the "User", has all the information regarding himself and can easily invoke the proper operation based on this knowledge. And you have to make use of inheritance.
In your case you would have to reflect the user types via simple inheritance or via composition. Your "User" would than have a property "UserType", like in your example, but instead of making it just an "Enum" like type, it becomes a complex type that inherits an "IUserType" interface and knows how to construct its specific dependencies ("UserType" implements "IUserType"). The "IUserType" could expose type specific attributes via a property (e.g. "IUserType.TypeSpecificTree" that returns an "ITypeSpecificTree").
So when in your example a "User" is promoted to premium, you would just set the property to a new instance of the concrete "IUserType" implementation (e.g. PremiumUserType") that executes its specific actions like building the premium tree (an "ITypeSpecificTree" implementation) from your example as well as constructing associated types.
This way the switch statement is eliminated by using composition and inheritance. We transformed the complex "UserType" property into a separate class and then moved type specific responsibilities to the type itself.
The inheritance and especially dependency inversion helped to operate on an object (e.g. getting the user type specific information like (User.IUserType.IUserSpecificTree") without knowing the concrete type. This helped to ensure that we are open for extension. Inheritance also helped to encapsulate type specific behavior to make our code close for modification.
If we need to make changes to how the type specific tree is generated or how this user type behaves, we would only touch the associated "IUserType" implementation but never the "User". If new user types are added (extension), they will have to implement the base interface "IUserType" and no other code, like switch-statements, must be touched to make it work and no more type checks are required.
And to make it complete and to offer some more extensibility, the "User" class should also implement an interface e.g. "IUser" that exposes the user type (e.g. "IUser.IUserType").