规约模式,颤抖吧产品经理!再也不怕你乱改需求了
大家好,今天来和大家聊聊规约模式。
规约模式的英文是Specification Pattern,这里的规约其实是一个表意的翻译,Specification直译过来是要求、技术说明、明确的意思。光看名字估计大家都是一脸懵逼,根本不知道这个设计模式大概会是一个什么样子。这也是设计模式的一个通病,就是内涵比较晦涩,很难通过名称来概括。
我们先来简单说说这里规约的意思,它的含义其实很简单,就是把代码当中的代码逻辑以及业务规则区分开。这样的话我们可以在不影响全局的情况下自由地修改和组合这两个部分。
我知道大家看完这个解释估计还是似懂非懂,没有关系,我们来简单看一个例子即可。
举个例子
我们都知道QQ里有各种钻石会员,什么绿钻、粉钻之类的。每种会员呢对应一些特殊的权益,假设现在我们新开发了某一个功能,要提供给同时拥有绿钻和粉钻的用户使用。
我们来实现这个逻辑非常简单,只需要一个判断条件就可以了。
def is_satisfied(user):
return user.isGreenAuth() and user.isPinkAuth()
但是这里有一个问题,这里的逻辑是写死的,时间久了之后,如果这个代码转交给其他人维护了,那么接手的人估计会一脸懵。根本不知道这里为什么要这么判断,也不知道这个函数代表的功能是什么,可能需要去翻很多代码或者是找很多文档才能解决疑惑。
这里的根本问题就是业务规则和实现逻辑混合在一起了,什么意思呢?这里的绿钻 + 粉钻的判断就是业务规则,是为了实现某项功能而制定的,产品经理要的是这个两者叠加的规则。而我们通过user.isGreenAuth() and user.isPinkAuth()来实现,相当于新建了一个规则,从功能上这当然没有问题。但是问题是这里的规则和代码是定死的。
假设说某一天产品经理提了一个新的需求,不仅需要绿钻 + 粉钻还需要年龄大于18岁,并且年收入超过10w才可以享受这个功能。那么带来的结果就是我们需要在这个is_satisfied函数当中加上许多代码,随着时间的推移这个函数当中的代码会变得越来越臃肿,并且很有可能以后这当中的一些判断边界需要修改,比如18岁改成20岁,比如10w改成30w等等,这些都是有可能的。当要修改的时候你会发现由于代码的耦合和混乱,改起来非常麻烦。
为了解决这个问题就需要引入规约。
规约的含义
规约的意思是把逻辑和规则区分开,规则的归规则,逻辑的归逻辑。
我们还用上面的例子来看,比如在新的需求当中,逻辑本身是很简单的。即粉钻 + 绿钻 + 年龄达标 + 收入达标,这个是规则,而年龄达标和收入达标则是具体的实现。
当我们把规则定好了之后,我们再去解构其中的组件,比如收入达标的定义是年收入大于10w,比如年龄达标的含义是大于18岁。我们可以把这些组件做成单独的模块,这样以后需要修改这些边界的时候,我们只需要在具体实现当中修改就可以了,对于规则的部分就不用动了。同样如果我们需要修改规则,我们也可以避免对实现的改动,这就是规约的意义。
首先,我们需要一个框架用来实现逻辑的组合。
在逻辑当中变量之间的关系只有三种,就是与或非。所以整个逻辑关系还是很清楚的。这里我们采取抽象的方法,先定义接口,再去做具体的实现。这是我们规约出来的条件,它当中有四个接口,分别是与或非的操作接口,以及一个is_satisfied的判断接口。
from abc import abstractmethod
class Specification:
def and_specification(self, candidate):
raise NotImplementedError()
def or_specification(self, candidate):
raise NotImplementedError()
def not_specification(self, candidate):
raise NotImplementedError()
@abstractmethod
def is_satisfied(self, candidate):
pass
在Specification的基础上,我们进一步实现与或非执行的逻辑。这里的逻辑很好理解,就不多解释了。
class AndSpecification(Specification):
_one = Specification()
_other = Specification()
def __init__(self, one, other):
self._one = one
self._other = other
def is_satisfied(self, candidate):
return bool(self._one.is_satisfied(candidate) and self._other.is_satisfied(candidate))
class OrSpecification(Specification):
_one = Specification()
_other = Specification()
def __init__(self, one, other):
self._one = one
self._other = other
def is_satisfied(self, candidate):
return bool(self._one.is_satisfied(candidate) or self._other.is_satisfied(candidate))
class NotSpecification(Specification):
_wrapped = Specification()
def __init__(self, wrapped):
self._wrapped = wrapped
def is_satisfied(self, candidate):
return bool(not self._wrapped.is_satisfied(candidate))
# 组合规则组件,也就是两个规约条件的逻辑组合
class CompositeSpecification(Specification):
@abstractmethod
def is_satisfied(self, candidate):
pass
def and_specification(self, candidate):
return AndSpecification(self, candidate)
def or_specification(self, candidate):
return OrSpecification(self, candidate)
def not_specification(self):
return NotSpecification(self)
上面所有的类都是为了定义规则的,这些有了之后,我们只需要在它的基础上填充具体的业务判断逻辑就可以了。相当于我们把一个复杂的业务逻辑拆分了,拆分成了若干个子逻辑的组合。
class User:
def __init__(self, age=18, incoming=0, green_auth=False, pink_auth=False):
self.age = age
self.incoming = incoming
self.green_auth = green_auth
self.pink_auth = pink_auth
class UserSpecification(CompositeSpecification):
def is_satisfied(self, candidate):
return isinstance(candidate, User)
class GreenUserSpecification(CompositeSpecification):
def is_satisfied(self, candidate):
return getattr(candidate, 'green_auth', False)
class PinkUserSpecification(CompositeSpecification):
def is_satisfied(self, candidate):
return getattr(candidate, 'pink_auth', False)
class UserAgeSpecification(CompositeSpecification):
def is_satisfied(self, candidate):
return getattr(candidate, 'age', 0) > 18
class UserIncomingSpecification(CompositeSpecification):
def is_satisfied(self, candidate):
return getattr(candidate, 'incoming', 0) > 10
这样我们就把具体的判断逻辑和规则剥离出来了,这样两者之间就互不影响了,最后我们来看下具体应用的例子。
ivan = User(green_auth=True)
lily = User(age=23, incoming=20, green_auth=True, pink_auth=True)
specification = UserSpecification().and_specification(PinkUserSpecification).and_specification(GreenUserSpecification).and_specification(UserAgeSpecification).and_specification(UserIncomingSpecification)
print(specification.is_satified(ivan))
print(specification.is_satified(lily))
这里的specification就是产品经理提的具体规则,如果以后需要修改我们只需要修改它的定义即可。如果某一条具体的判断逻辑需要变动,我们找到对应的代码改动,就不会影响逻辑以及其他业务代码了。
之前有大牛曾经说过,某种意义上来说设计模式的诞生,其实是为了应对产品经理们朝三暮四的需求变动而产生的。不知道大家看完这个例子有没有这样的感触,至少我是觉得挺有道理的。
好了,今天的文章就到这里,衷心祝愿大家每天都有所收获。如果还喜欢今天的内容的话,请来一个三连支持吧~(点赞、关注、转发)