大家好,今天来和大家聊聊规约模式。
规约模式的英文是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就是产品经理提的具体规则,如果以后需要修改我们只需要修改它的定义即可。如果某一条具体的判断逻辑需要变动,我们找到对应的代码改动,就不会影响逻辑以及其他业务代码了。
之前有大牛曾经说过,某种意义上来说设计模式的诞生,其实是为了应对产品经理们朝三暮四的需求变动而产生的。不知道大家看完这个例子有没有这样的感触,至少我是觉得挺有道理的。
好了,今天的文章就到这里,衷心祝愿大家每天都有所收获。如果还喜欢今天的内容的话,请来一个三连支持吧~(点赞、在看、转发)