python3--归一化设计,接口类和抽象类,接口隔离原则,多态

发布时间:2018-04-16 16:32:51编辑:Run阅读(3797)

    抽象类与接口类

    继承有两种用途

    1 继承基类的方法,并且做出自己的改变或者扩展(代码重用)

    2 声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能

    class Alipay:
        def pay(self, money):
            print('支付宝支付了{}元'.format(money))
    
    class Applepay:
        def pay(self, money):
            print('apple pay支付了{}元'.format(money))
    
    
    def pay(payment, money):
        '''
        支付函数,总体负责支付
        对应支付的对象和要支付的金额
        '''
        payment.pay(money)
    
    ali = Alipay()
    app = Applepay()
    pay(ali, 100)
    pay(app, 200)

    执行结果

    支付宝支付了100元

    apple pay支付了200元


    错误示例:实际中开发容易出现的问题

    class Alipay:
        def pay(self, money):
            print('支付宝支付了{}元'.format(money))
    
    class Applepay:
        def pay(self, money):
            print('apple pay支付了{}元'.format(money))
    
    class Wechatpay:
        def fuqian(self, money):
            print('微信支付了{}元'.format(money))
    
    def pay(payment, money):
        '''
        支付函数,总体负责支付
        对应支付的对象和要支付的金额
        '''
        payment.pay(money)
    
    ali = Alipay()
    app = Applepay()
    we = Wechatpay()
    pay(ali, 100)
    pay(app, 200)
    pay(we, 300)

    执行结果,因为实例化Wechatpay后,里面并没有pay这个方法

    AttributeError: 'Wechatpay' object has no attribute 'pay'


    为了避免这个问题,可以创建一个规范,需要借用abc模块来实现接口

    from abc import ABCMeta,abstractmethod
    class Payment(metaclass=ABCMeta):
        @abstractmethod
        def pay(self, money):
            pass
    
    class Alipay(Payment):
        def pay(self, money):
            print('支付宝支付了{}元'.format(money))
    
    class Applepay(Payment):
        def pay(self, money):
            print('apple pay支付了{}元'.format(money))
    
    class Wechatpay(Payment):
        def fuqian(self, money):
            print('微信支付了{}元'.format(money))
    
    def pay(payment, money):
        '''
        支付函数,总体负责支付
        对应支付的对象和要支付的金额
        '''
        payment.pay(money)
    
    we = Wechatpay()  # 实例化这里就报错了

    执行结果,无法用抽象方法实例化抽象类Wechatpay支付

    TypeError: Can't instantiate abstract class Wechatpay with abstract methods pay


    说明,在继承Payment类的子类中,必须有一个pay方法,把上面代码改成

    from abc import ABCMeta,abstractmethod
    class Payment(metaclass=ABCMeta):
        @abstractmethod
        def pay(self, money):
            pass
    
    class Alipay(Payment):
        def pay(self, money):
            print('支付宝支付了{}元'.format(money))
    
    class Applepay(Payment):
        def pay(self, money):
            print('apple pay支付了{}元'.format(money))
    
    class Wechatpay(Payment):
        def pay(self, money):
            print('微信支付了{}元'.format(money))
    
    def pay(payment, money):
        '''
        支付函数,总体负责支付
        对应支付的对象和要支付的金额
        '''
        payment.pay(money)
    
    we = Wechatpay()  
    pay(we, 300)

    执行结果

    微信支付了300元


    实践中,继承的第一种含义意义并不很大。甚至常常是有害的,因为它使得子类与基类出现强耦合

    继承的第二种含义非常重要,它又叫接口继承

    接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”----这在程序设计上,叫做归一化


    归一化使得高层的外部调用者可以不加区分的处理所有接口兼容的对象集合

    依赖倒置原则:

    高层模块不应该依赖底层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。换言之,要针对接口编程,而不是针对实现编程


    接口提取了一群类共同的函数,可以把接口当做一个函数的集合。

    然后让子类去实现接口中的函数

    这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。

    归一化,让使用者无需关心对象的类是什么,只需要知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。


    抽象类

    什么是抽象类

    与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化


    为什么要有抽象类

    如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。

    从现实角度来看,抽象类与普通类的不同之处在于:抽象类中有抽象方法,该类不能被实例化,只能被继承,且子类必须实现抽象方法,这一点与接口类有点类似,但其实是不同的

    在python中实现抽象类

    import abc
    class All_file(metaclass=abc.ABCMeta):
        all_type = 'file'
        @abc.abstractmethod  # 定义抽象方法,无需实现功能
        def read(self):
            '''子类必须定义读功能'''
            pass
    
        @abc.abstractmethod  # 定义抽象方法,无需实现功能
        def write(self):
            '''子类必须定义写功能'''
            pass
    
    class Txt(All_file):  # 子类继承抽象类,但是必须定义read和write方法
        def read(self):
            print('文本数据的读取方法')
    
        def write(self):
            print('文本数据的写入方法')
    
    class Sata(All_file):
        def read(self):
            print('硬盘数据的读取方法')
    
        def write(self):
            print('硬盘数据的写入方法')
    
    class Process(All_file):
        def read(self):
            print('进程数据的读取方法')
    
        def  write(self):
            print('进程数据的写入方法')
    
    txt = Txt()  # 实例化
    sata = Sata()  # 实例化
    process = Process()  # 实例化
    
    # 这样情况,就是归一化设计
    txt.read()
    sata.write()
    process.read()
    print(txt.all_type)
    print(sata.all_type)
    print(process.all_type)

    执行结果

    文本数据的读取方法

    硬盘数据的写入方法

    进程数据的读取方法

    file

    file

    file


    抽象类与接口类

    抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read,write),而接口只强调函数属性的相似性。

    抽象类是一个介于类和接口之间的一个概念,同时具备类和接口的部分特性,可以用来实现归一化的设计

    1 多继承问题

    在继承抽象类的过程中,我们应该尽量避免多继承

    而在继承接口的时候,我们反而鼓励你来多继承接口

    接口隔离原则

    使用多个专门的接口,而不使用单一的总接口。即客户端不应该依赖那些不需要的接口


    2 方法的实现

    在抽象类中,我们可以对一些抽象方法做出基础实现

    而在接口类中,任何方法都只是一种规范,具体的功能需要子类实现


    from abc import ABCMeta,abstractmethod
    class FlyAnimal(metaclass=ABCMeta):
        @abstractmethod
        def fly(self):pass
        @abstractmethod
        def cal_flying_speed(self):pass
        @abstractmethod
        def cal_flying_height(self):pass
    class WalkAnimal(metaclass=ABCMeta):
        @abstractmethod
        def walk(self):pass
    class SwimAnimal(metaclass=ABCMeta):
        @abstractmethod
        def walk(self):pass
        
    class Tiger(WalkAnimal,SwimAnimal):
        def walk(self):pass
        def swim(self):pass
        
    class Monkey:
        def walk(self):pass
        def climb(self):pass
        
    class Swan(FlyAnimal,WalkAnimal,SwimAnimal):
        def swim(self):pass
        def walk(self):pass
        def fly(self):pass
        def cal_flying_speed(self):pass
        def cal_flying_height(self):pass
        
    class Parrot(FlyAnimal):
        def fly(self):pass
        def cal_flying_speed(self):pass
        def cal_flying_height(self): pass
    
    # 所有会飞的动物,具有一些会飞的动物特性
    # 所有会走的动物,具有一些会走的动物特性

    接口类的作用

    在java中,能够满足接口隔离原则,且完成多继承的约束

    而在python中,满足接口隔离原则,由于python本身支持多继承,所以就不需要接口的概念了


    抽象类和接口类

    在python中

    并没有什么不同,都是用来约束子类中的方法的

    只要是抽象类和接口类中被abstractmethod装饰的方法,都需要被子类实现

    需要注意的是,当多个类之间有相同的功能也有不同的功能的时候,应该采用多个接口类来进行分别的约束


    强类型语言

    java,c++,C#


    弱类型语言

    shell


    介于强类型与弱类型之间---python动态强类型语言,


    多态性

    什么是多态动态绑定(在继承的背景下使用时,有时也称为多态性)

    多态性是指在不考虑实例类型的情况下使用实例

    class Payment:
        def pay(self):pass
    
    class QQpay(Payment):
        def pay(self, money):
            print('使用qq支付了%s元'%money)
    
    class Wechatpay(Payment):
        def pay(self, money):
            print('使用微信支付了%s元'%money)
    
    qq = QQpay()
    we = Wechatpay()
    def pay(pay_obj, money):
        pay_obj.pay(money)
    pay(qq, 100)
    pay(we, 200)

    执行结果

    使用qq支付了100元

    使用微信支付了200元


    多态和鸭子类型

    多态 通过继承实现

    在python中不需要刻意实现多态,因为python本身自带多态效果


    鸭子类型

    Python崇尚鸭子类型,即‘如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子’

    python程序员通常根据这种行为来编写程序。例如,如果想编写现有对象的自定义版本,可以继承该对象

    也可以创建一个外观和行为像,但与它无任何关系的全新对象,后者通常用于保存程序组件的松耦合度

    例子

    #二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用
    class TxtFile:
        def read(self):
            pass
    
        def write(self):
            pass
    
    class DiskFile:
        def read(self):
            pass
        def write(self):
            pass

    总结:多态和鸭子类型

    多态 通过继承实现

    java在一个类之下发展出来的多个类的对象都可以作为参数传入一个函数或者方法

    python中不需要


    鸭子类型

    不是通过具体的继承关系来约束某些类中必须有哪些方法名,是通过一种约定俗成的概念来保证在多个类中相似的功能叫相同的名字

关键字