将业务逻辑放入扩展方法
Putting business logic into extension methods
有第三方类型Person
。它只是公开了 Subscriptions
集合,这是一个人订阅的邮件列表列表:
public class Person
{
public Person(int id)
public IList<int> Subscriptions {get;set;}
}
(是mutable,没有任何验证,大家可以自行清理,但那是第三方API)。
我需要放置某种订阅业务逻辑并寻找合适的位置(订阅可能有一些验证规则,它们可能会根据某些条件自动添加或删除等)。我可以在这里看到两个选项:
选项 1:
在我的解决方案中,这可能是一个新的 Recipient
类型,所有业务逻辑都封装在 Subscribe
和 Unsubscribe
方法中:
public class Recipient
{
public Recipient(Person person) { ... }
public void SubscribeTo(int mailingListId) { ... }
public void UnsubscribeFrom(int mailingListId) { ... }
}
选项 2:
同样的东西但是变成了 PersonExtensions
class:
public static class PersonExtensions
{
public static void SubscribeTo(this Person person, int mailingListId) { ... }
public static void UnsubscribeFrom(this Person person, int mailingListId) { ... }
}
静态扩展方法可能会阻止我创建大量 Recepient
个实例,但看起来不是放置 BL 的合适位置。这样做有什么缺点吗?
在我看来,您绝对应该选择选项 1。扩展方法不是用来处理业务逻辑的;这应该总是发生在 class 中。我认为将 Person
class 的关注点与 Recipient
人的关注点分开是正确的。如果您正在考虑 SOLID design principles.
,那么您肯定在选项 1 的正确轨道上
Single responsibility,IE一个class应该只有一个responsibility(一个"reason to change")。与您关于使用扩展方法代替 classes 的问题无关,但它可以通过将人与收件人分开来证明您在正确的轨道上。如果您的收件人逻辑发生变化,您不必修改每一段使用 person 但不关心收件人逻辑的代码。
Open/closed -- 对扩展开放,对修改关闭。 (不要让 "extension" 这个词的使用让您想到扩展方法。)您应该始终能够添加您的 (preferably-abstract) [=47= 的扩展 (sub-classes) ] class。 object-oriented 编程的基本原则(如多态)在这里发挥作用,none 如果选项 2 可行的话。
Liskov 替换 dove-tails 很好地符合 open/closed 原则,因为它允许您替换代码中需要你的 parent class 的实例和你的新 "extended" sub-class.
的实例
None 如果您将所有逻辑放在一个带有扩展方法的静态 class 中,这是可能的。
我不同意所选的答案。
业务规则经常更改并将它们添加到域实体中,将易变逻辑与可能几乎不可变的对象紧密耦合。如果明天您需要添加更多规则或由于业务条件发生变化而更改规则,会发生什么情况?或者如果相同的规则以不同的方式适用于不同的地理区域怎么办?您将需要一次又一次地干预您的对象,添加或删除方法并提供增加的复杂性,从而和冗长。这是当今大多数行业中很可能出现的情况。而且,那些方法很可能是public。因此任何人都可能以错误的方式使用它们,除非您围绕它们构建复杂的应用程序逻辑。
解决方案是将业务规则与域对象分离,委托第三个对象负责决定如何组合它们以及如何(以及何时)应用它们。这就是业务规则引擎发挥作用的地方。它们允许您创建 类 封装任何域实体的业务规则,将它们添加到规则存储库并根据需要触发它们(因此您真的必须知道自己在做什么)。另一个优势是您可以让您的域实体保持轻便和简单,不受业务逻辑的冗长和复杂性的影响。
更高级的引擎使用复杂的算法(如 rete 算法),能够自行计算出(大多数时候)应用规则的确切顺序并支持依赖注入。我个人使用 NRules:它是一个开源的企业级规则引擎,快速可靠,建立在 rete 算法之上。
https://github.com/NRules/NRules
试一试,告诉我 ;)
有第三方类型Person
。它只是公开了 Subscriptions
集合,这是一个人订阅的邮件列表列表:
public class Person
{
public Person(int id)
public IList<int> Subscriptions {get;set;}
}
(是mutable,没有任何验证,大家可以自行清理,但那是第三方API)。
我需要放置某种订阅业务逻辑并寻找合适的位置(订阅可能有一些验证规则,它们可能会根据某些条件自动添加或删除等)。我可以在这里看到两个选项:
选项 1:
在我的解决方案中,这可能是一个新的 Recipient
类型,所有业务逻辑都封装在 Subscribe
和 Unsubscribe
方法中:
public class Recipient
{
public Recipient(Person person) { ... }
public void SubscribeTo(int mailingListId) { ... }
public void UnsubscribeFrom(int mailingListId) { ... }
}
选项 2:
同样的东西但是变成了 PersonExtensions
class:
public static class PersonExtensions
{
public static void SubscribeTo(this Person person, int mailingListId) { ... }
public static void UnsubscribeFrom(this Person person, int mailingListId) { ... }
}
静态扩展方法可能会阻止我创建大量 Recepient
个实例,但看起来不是放置 BL 的合适位置。这样做有什么缺点吗?
在我看来,您绝对应该选择选项 1。扩展方法不是用来处理业务逻辑的;这应该总是发生在 class 中。我认为将 Person
class 的关注点与 Recipient
人的关注点分开是正确的。如果您正在考虑 SOLID design principles.
Single responsibility,IE一个class应该只有一个responsibility(一个"reason to change")。与您关于使用扩展方法代替 classes 的问题无关,但它可以通过将人与收件人分开来证明您在正确的轨道上。如果您的收件人逻辑发生变化,您不必修改每一段使用 person 但不关心收件人逻辑的代码。
Open/closed -- 对扩展开放,对修改关闭。 (不要让 "extension" 这个词的使用让您想到扩展方法。)您应该始终能够添加您的 (preferably-abstract) [=47= 的扩展 (sub-classes) ] class。 object-oriented 编程的基本原则(如多态)在这里发挥作用,none 如果选项 2 可行的话。
Liskov 替换 dove-tails 很好地符合 open/closed 原则,因为它允许您替换代码中需要你的 parent class 的实例和你的新 "extended" sub-class.
的实例None 如果您将所有逻辑放在一个带有扩展方法的静态 class 中,这是可能的。
我不同意所选的答案。 业务规则经常更改并将它们添加到域实体中,将易变逻辑与可能几乎不可变的对象紧密耦合。如果明天您需要添加更多规则或由于业务条件发生变化而更改规则,会发生什么情况?或者如果相同的规则以不同的方式适用于不同的地理区域怎么办?您将需要一次又一次地干预您的对象,添加或删除方法并提供增加的复杂性,从而和冗长。这是当今大多数行业中很可能出现的情况。而且,那些方法很可能是public。因此任何人都可能以错误的方式使用它们,除非您围绕它们构建复杂的应用程序逻辑。
解决方案是将业务规则与域对象分离,委托第三个对象负责决定如何组合它们以及如何(以及何时)应用它们。这就是业务规则引擎发挥作用的地方。它们允许您创建 类 封装任何域实体的业务规则,将它们添加到规则存储库并根据需要触发它们(因此您真的必须知道自己在做什么)。另一个优势是您可以让您的域实体保持轻便和简单,不受业务逻辑的冗长和复杂性的影响。
更高级的引擎使用复杂的算法(如 rete 算法),能够自行计算出(大多数时候)应用规则的确切顺序并支持依赖注入。我个人使用 NRules:它是一个开源的企业级规则引擎,快速可靠,建立在 rete 算法之上。
https://github.com/NRules/NRules
试一试,告诉我 ;)