重载了开放/封闭原则的例子吗?


12

维基百科说

“软件实体(类,模块,功能等)应打开以进行扩展,但应关闭以进行修改”

函数一词引起了我的注意,现在我想知道是否可以假定为方法创建重载可以视为“打开/关闭”原理的示例吗?

让我解释一个例子。考虑一下您的服务层中有一个方法,该方法在将近1000个地方中使用。该方法获取userId并确定用户是否为admin:

bool IsAdmin(userId)

现在考虑在某个地方,有必要根据用户名而不是userId来确定用户是否为admin。如果我们更改上述方法的签名,那么我们已经在1000个地方破坏了代码(功能应禁止修改)。因此,我们可以创建一个重载来获取用户名,基于用户名找到userId以及原始方法:

public bool IsAdmin(string username)
{
    int userId = UserManager.GetUser(username).Id;
    return IsAdmin(userId);
}

这样,我们通过为其创建重载扩展了功能(功能应可扩展)。

它是一个开放/封闭原则的例子吗?

Answers:


5

因此,我将亲自解释Wiki声明:

  • 对于一个类:可以随时继承该类并覆盖或 扩展其功能,但不要乱砍原始类,从而改变其功能。
  • 对于模块(例如,可能是一个库):可以随时编写一个新的模块/库来包装原始模块,将功能合并为易于使用的版本,或使用其他功能扩展原始模块 ,但不要更改原始模块。
  • 对于一个函数(即静态函数,不是类方法):您的示例对我而言是合理的;在新的IsAdmin(string)中重用原始的IsAdmin(int)函数。原来的不变,新的功能扩展了它的功能。

但是,如果您的代码使用的是IsAdmin(int)所在的'cUserInfo'类,那实际上就是在打破规则并更改该类。可悲的是,只有当您创建了新类cUserWithNameInfo:public cUserInfo类并将IsAdmin(string)覆盖在那里时,该规则才会保留。如果我拥有代码库,我将永远不会遵守规则。我会说胡话,然后做出您建议的更改。


3

首先,不幸的是您的示例是人为的。在现实世界中,您永远都不会这样做,因为这会导致不必要的重复获取。或者,更糟糕的是,因为在某些时候userid和username可能变成相同的类型。而不是

public bool IsAdmin(int userid)
{
    User user = UserManager.GetUser(userid);
    return user.IsAdmin();
}

public bool IsAdmin(string username)
{
    int userId = UserManager.GetUser(username).Id;
    return IsAdmin(userId);
}

您应该真正扩展该类。

public bool IsAdmin(int userid)
{
    User user = UserManager.GetUserById(userid);
    return user.IsAdmin();
}

public bool IsUsernameAdmin(string username)
{
    User userId = UserManager.GetUserByName(username);
    return user.IsAdmin();
}

您可以进一步重构并将第一个方法名称更改为IsUserIdAdmin,以保持一致性,但这不是必需的。关键是,在添加新方法并确保不会破坏任何调用代码的情况下,您发现自己的类可扩展。或者换句话说,开放扩展

这就是开闭原则。在尝试扩展部分代码并发现自己不得不进行修改(随之而来的风险增加)之前,您永远不会真正知道代码的符合程度。但是,有了经验,您就会学会预测。

正如鲍伯叔叔说的(强调我的):

由于关闭无法完成,因此必须具有战略意义。也就是说,设计师必须选择与之相对应的更改类型,以结束其设计。这需要从经验中得出一定的先见之明。经验丰富的设计师非常了解用户和行业,可以判断各种变化的可能性。然后,他确保对最可能的更改调用开闭原理。


感谢您的良好解释。但是,获取1000次往返或一次往返并不是这里的重点。因此,让我们暂时忘记性能。但是,我所做的也是扩展类,因为我已经向它添加了另一个方法,尽管名称相同,但输入参数却不同。但是我非常感谢您鲍伯叔叔的话。+1。;)
Saeed Neamati 2012年

@SaeedNeamati:我的意思是,无论您要添加使用现有方法的方法还是添加完全独立的方法,无论您的类是否可以扩展。但是,如果您必须修改现有的方法接口以实现此目的,那么您就不会修改。
pdr 2012年

2

符合开闭原理的模块具有两个主要属性。

  1. 它们是“开放扩展”。这意味着可以扩展模块的行为。我们可以使模块随着应用程序需求的变化或满足新应用程序的需求而以新的和不同的方式运行。
  2. 它们是“封闭修改”。这样的模块的源代码不正确。不允许任何人对其进行源代码更改。
  1. 通过重载方法,您可以扩展现有模块的功能,从而满足应用程序的新需求

  2. 您不会对现有方法进行任何更改,因此不会违反第二条规则。

我想听听其他人关于不允许更改原始方法的看法。是的,您可以放置​​适当的访问修饰符并强制执行封装,但是还有什么可以做的呢?

参考:http : //www.objectmentor.com/resources/articles/ocp.pdf


1

开闭原则是一个目标,是一个理想的案例,并不总是现实。尤其是在寻求重构旧代码时,第一步通常是大量修改,以使OCP成为可能。原理的根源是工作代码已经可以工作,并且更改可能会引入错误。因此,最佳方案是不更改现有代码,而仅添加新代码。

但是,假设您有一个名为的函数BigContrivedMethod(int1, int2, string1)BigContrivedMethod做三件事:thing1,thing2和thing3。在这一点上,重用BCM可能很困难,因为它做的太多了。将其重构(如果可能)为ContrivedFunction1(int),,ContrivedFunction2(int)ContrivedFunction3(string)为您提供三个更小,更集中的方法,您可以更轻松地合并它们。

这就是OCP在方法/功能方面的关键:组成。您可以通过从其他函数调用它们来“扩展”函数。

请记住,OCP是其他5条原则(SOLID准则)的一部分。第一个关键是单一责任。如果您的代码库中的所有内容仅完成了它必须要做的一件特定的事情,那么您就无需修改代码。您只需要添加新代码或以新方式将旧代码组合在一起。由于实际代码很少符合该准则,因此您通常必须对其进行修改以获取SRP,然后才能获得OCP。


我认为您在这里谈论的是单一职责负责人,而不是OCP。
Saeed Neamati 2012年

1
我的意思是,没有SRP,您将无法拥有OCP。太多的代码无法扩展,也无法重用。SOLID几乎是按重要性顺序编写的,每种原则在可行之前都依赖于它。
CodexArcanum'5

这个答案应该更多。
Ashish Gupta

0

如果您通过编写新代码而不是更改旧代码来更改程序的行为,则您将遵循开闭原则。

由于编写对所有可能的变化都开放的代码是完全不可能的(并且由于进入分析瘫痪状态而不想这样做),因此您编写的代码通过扩展而不是修改来响应当前正在处理的所有不同行为。

当您遇到需要更改的内容时,您不仅可以找出更改的目的,还可以更改程序的结构,而无需更改其行为,从而可以通过编写新代码来更改该行为,而不是简单地更改内容以允许新行为。

因此,这如何适用于您的情况:

如果要向类中添加新函数,则您的类不会打开/关闭,但是您要重载的函数的客户端却是打开/关闭的。

如果您只是添加适用于您的类的新函数,那么它将与您的函数的客户端同时打开/关闭。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.