面向对象之类的约束

Posted zzliu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象之类的约束相关的知识,希望对你有一定的参考价值。

先来看一个例子:如果我们要写一个支付接口,怎么做呢

版本一:

class QQpay:
    def pay(self, money):
        print("此次消费%s" % money)


class Alipay:
    def pay(self, money):
        print("此次消费%s" % money)


a1 = QQpay()
a2 = Alipay()
a1.pay(100)
a2.pay(200)

版本一虽然完成了支付功能,但是不同的支付没有统一,使用QQ支付时调用的是QQpay类的pay方法,使用Alipay支付的时候又变成调用Alipay类的pay方法,有没有办法使之统一呢,来看版本二。

版本二:

class QQpay:      
    def pay(self, money):
        print("此次消费%s" % money)


class Alipay:
    def pay(self, money):
        print("此次消费%s" % money)

def pay(obj, money): # 统一规范 obj.pay(money) a1 = QQpay() a2 = Alipay() pay(a1, 100) pay(a2, 200)

版本二通过在类外面定义了一个pay方法,然后通过pay方法去调用QQpay类和Alipay类的方法,这样就统一了接口,但是这里有一个问题,如果程序之后遇到升级,需要新加入一个微信支付的功能,但是接手这个项目的人这样写........

class Alipay:
    def pay(self, money):
        print("此次消费%s" % money)


class Wechatpay:      # 野生程序员写的
    def zhifu(self, money):
        print("此次消费%s" % money)


def pay(obj, money):    # 统一规范
    obj.pay(money)


a1 = QQpay()
a2 = Alipay()
a3 = Wechat()
pay(a1, 100)
pay(a2, 200)
pay(a3, 300)   # 报错

很明显,执行pay(a3, 300)时会报错,因为Wechatpay里面并没有pay方法。我们当然可以通过事后更改代码的方法来解决这个问题,但是有没有从程序设计的层面上解决这个问题的方法呢,答案是有的

版本三:

class Pay:    # 定义一个基类Pay,所有支付的类都继承这个类
    def pay(self, money):
        pass


class QQpay(Pay):       
    def pay(self, money):
        print("此次消费%s" % money)


class Alipay(Pay):
    def pay(self, money):
        print("此次消费%s" % money)


class Wechatpay(Pay):      
    def pay(self, money):
        print("此次消费%s" % money)


def pay(obj, money):
    obj.pay(money)


a1 = QQpay()
a2 = Wechatpay()
a3 = Alipay()
pay(a1, 100)
pay(a2, 200)
pay(a3, 300)

版本三解决了支付名字不统一带来的报错问题,但是如果接手的程序员不按规定在自己写的类里定义pay方法,程序虽然不会报错但也不会正常支付,确切地说,版本三只是弱约束,正经的程序员看到了定义了一个基类Pay,其他支付的类都继承了Pay,就会知道这个Pay是统一接口用的,在自己的类里要定义一个pay方法,但是有些程序员比较有个性,就要取别的名字,这时版本三就无能无力了,因此我们对版本三进行了升级

版本四:

class Pay:
    def pay(self, money):
        raise NotImplementedError("未定义pay方法")   # 抛出异常


class QQpay(Pay):       # 正规写法
    def pay(self, money):
        print("此次消费%s" % money)


class Alipay(Pay):
    def pay(self, money):
        print("此次消费%s" % money)


class Wechatpay(Pay):      # 野生写法
    def pay(self, money):
        print("此次消费%s" % money)


class Unionpay(Pay):
    def zhifu(self, money):
        print("此次消费%s" % money)


def pay(obj, money):
    obj.pay(money)


a1 = QQpay()
a2 = Wechatpay()
a3 = Alipay()
a4 = Unionpay()
pay(a1, 100)
pay(a2, 200)
pay(a3, 300)
pay(a4, 400)     # NotImplementedError: 未定义pay方法

这里我们在基类的pay方法里面加上 raise Exception("未定义pay方法"),当执行pay(a4, 400)时,会先在Unionpay里面找,很显然找不到,之后会在其父类里面找,然后执行父类的pay方法,这时就会执行raise Exception("未定义pay方法"),这句话的意思是,抛出异常,异常的名字是Exception,异常的内容是"未定义pay方法"。版本四的核心就是在父类里定义一个pay方法,方法里只有抛出错误的语句,各子类继承父类,子类里必须定义名字相同的pay方法,否则执行时就会报错。还有一种从Java和C#继承过来的方法:运用抽象类(接口类)来解决

版本五:

from abc import ABCMeta, abstractmethod


class Pay(metaclass=ABCMeta):    # 这个父类定义了一个约束、规范,规定子类一定要有pay方法
    """
    抽象类(接口类),制定一个规范,强制执行
    """
    @abstractmethod
    def pay(self, money):
        pass


class Alipay(Pay):
    def pay(self, money):
        print("此次消费%s" % money)


class Wechatpay(Pay):
    def zhifu(self, money):
        print("此次消费%s" % money)


def pay(obj, money):
    obj.pay(money)


a1 = Alipay()
a2 = Wechatpay()  # TypeError: Can‘t instantiate abstract class Wechatpay with abstract methods pay
pay(a1, 100)
pay(a2, 200)

总结:

1. 约束就是父类对子类的约束,子类必须要写XXX方法

2. 在python中实现约束有两种方法

  (1)人为抛出异常的方法,并且尽量抛出的是NotImplementedError. 这样比较专业, ?且错误比较明确.(推荐)

  (2)使用抽象类和抽象方法,由于该方案来源于Java和C#,所以还是用的比较少

 



以上是关于面向对象之类的约束的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段9——JS中的面向对象编程

代码学习PHP面向对象之类与对象

java基础面向对象之类与对象

python 面向对象约束 异常处理 MD5 日志处理

Python面向对象之类的封装继承与多态

面向对象之:封装,多态,以及类的约束