接口:从协议到抽象基类

  封装(Encapsulation)、继承(Inheritance)、多态(Polymorphism)

 

(1)封装(Encapsulation):类包含了数据和方法,将数据和方法放在一个类中就构成了封装。

(2)继承(Inheritance):Java是单继承的(这点和C++有区别),意味着一个类只能继承于一个类,被继承的类叫父类(或者叫基类,base class),继承的类叫子类。Java中的继承使用关键字extends。但是,一个类可以实现多个接口,多个接口之间用逗号进行分割。实现接口使用关键字implements。

(3)多态(Polymorphism):多态最核心的思想就是,父类的引用可以指向子类的对象,或者接口类型的引用可以指向实现该接口的类的实例。

 

“ 开闭 ”原则:

  对扩展开放:允许新增子类;对修改封闭:不需要修改依赖该类型的函数。 

  把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。

 

鸭子类型:

  不关注对象的类型,而关注对象的行为(方法)。它的行为是鸭子的行为,那么可以认为它是鸭子。

  调用不同的子类将会产生不同的行为,而无须明确知道这个子类实际上是什么,这是多态的重要应用场景

  鸭子类型是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。

  这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试。

  “鸭子测试”可以这样表述:“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”

class Duck():
    def walk(self):
        print('I walk like a duck')
    def swim(self):
        print('i swim like a duck')

class Person():
    def walk(self):
      print('this one walk like a duck') 
    def swim(self):
      print('this man swim like a duck')

  Person类拥有跟Duck类一样的方法,当有一个函数调用Duck类,并利用到了两个方法walk()和swim()。

  我们传入Person类也一样可以运行,函数并不会检查对象的类型是不是Duck,只要他拥有walk()和swim()方法,就可以正确的被调用。

 

接口:

  接口里有什么方法,继承类就必须有什么方法,接口中不能有任何功能代码。

  面向对象中的继承有两种用途:

    1)可以通过继承做到代码重用,并完成扩展;

    2)接口继承。定义一个接口类 Interface,接口类中定义了一些接口(函数,但这些函数都没有具体的实现),子类继承接口类,并且实现接口中的功能

class Operate_database():    # 接口类
    def query(self, sql):
        raise NotImplementedError

    def update(self, sql):
        raise NotImplementedError

class Operate_mysql(Operate_database):
    def query(self, sql):
        print('query mysql : %s' % sql)

    def update(self, sql):
        print('query mysql : %s' % sql)

class Operate_pg(Operate_database):
    def query(self, sql):
        print('query postgresql : %s' % sql)

    def update(self, sql):
        print('update postgresql : %s' % sql)

def query_data(operate_obj, sql):
    operate_obj.query(sql)

def update_data(operate_obj, sql):
    operate_obj.update(sql)

query_data(Operate_mysql(), 'select ...')    # query mysql : select ...
update_data(Operate_pg(), 'update...')    # update postgresql : update...
接口类

  若子类继承了Operate_database 接口类,但是没有实现其中的某一个方法的功能,调用时就会报错

  子类覆盖父类中的方法时,要注意方法名需要与父类中的方法名相同,且方法的参数个数与参数名也要相同

  这里更好的方式是通过 abc模块 来实现接口

from abc import ABCMeta,abstractmethod

class Operate_database(metaclass=ABCMeta):    # 接口类
    @abstractmethod
    def query(self, sql):
        pass

    @abstractmethod
    def update(self, sql):
        pass

class Operate_oracle(Operate_database):
    # 没有实现 query 方法
    def update(self, sql):
        print('update oracle : %s' % sql)

def query_data(operate_obj, sql):
    operate_obj.query(sql)

oracle = Operate_oracle()        # 由于没有实现接口中的所有方法,在这一步就会报错
query_data(oracle, 'select ...')
abc模块

▲ 在其他的语言里,比如Java,继承类没有重写接口方法是会报错的,而在python里不会,就是因为python没这个类型,所以只是在我们编程过程的一个规定,以I开头的类视为接口

 

抽象类:

  抽象类和接口类一样是一种规范,规定子类应该具备的功能。

  在Python中,抽象类和接口类没有明确的界限。

  若是类中所有的方法都没有实现,则认为这是一个接口类,若是有部分方法实现,则认为这是一个抽象类。

  抽象类和接口类都仅用于被继承,不能被实例化

 

  声明抽象基类最简单的方式是继承 abc.ABC 或其他抽象基类。

  然而,abc.ABC 是 Python 3.4 新增的类,因此如果你使用的是旧版Python,那么无法继承现有的抽象基类。

  此时,必须在 class 语句中使用 metaclass= 关键字,把值设为 abc.ABCMeta(不是 abc.ABC)

class Tombola(metaclass=abc.ABCMeta):    # Python 3
 # ...

class Tombola(object):                   # Python 2
    __metaclass__ = abc.ABCMeta
 # ...  

  抽象类,可以说是类和接口的混合体,既可以定义常规方法,也可以约束子类的方法(抽象方法)

from abc import ABCMeta,abstractmethod

class Operate_database(metaclass=ABCMeta):    # 抽象类

    log_path = '/tmp/db.log'

    def connect(self):
        print('connect db ...')

    @abstractmethod
    def query(self, sql):
        pass

    @abstractmethod
    def update(self, sql):
        pass
抽象类

 

协议:

  (1)协议是非正式的接口,是一组方法,Python没有interface 关键字,定义接口只是一个人为约定。

  (2)Python中存在多种协议,用于实现鸭子类型(对象的类型无关紧要,只要实现了特定的协议(一组方法)即可)。

  (3)需要成为相对应的鸭子类型,那就实现相关的协议,即相关的__method__。例如实现序列协议(__len__和getitem),这个类就表现得像序列。

  (4)可以根据具体场景实现一个具体协议的一部分。例如,为了支持迭代,只需实现__getitem__,不需要实现__len__。

  (5)在Python文档中,如果看到“文件类对象“(表现得像文件的对象),通常说的就是协议,这个对象就是鸭子类型。这是一种简短的说法,意思是:“行为基本与文件一致,实现了部分文件接口,满足上下文相关需求的东西。”

import collections

Card = collections.namedtuple('Card', ['rank','suit'])

class FrenchDeck:
    ranks = [str(n) for n in range(2, 11)] + list('JQKA')
    suits = 'spades diamonds clubs hearts'.split()

    def __init__(self):
        self._cards = [Card(rank, suit) for suit in self.suits
                                        for rank in self.ranks]
        
    def __len__(self):
        return len(self._cars)

    def __getitem__(self, position):
        return self._cards[position]

# Python的序列协议只需要__len__和__getitem__两个方法。
序列协议

 

DI(依赖注入):

  在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。

  比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象;

  A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。

  在系统运行时,会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。(以参数的形式传入

  A需要依赖 Connection才能正常运行,而这个Connection是通过注入到A中的,依赖注入的名字就这么来的。

from flask import Flask

app = Flask("example")

class DAO:
    def __init__(self):
        self.data = []

dao = DAO()

@app.route("/")
def m():
    return dao.data

if __name__ == "__main__":
    app.run()
没有使用DI
from flask import Flask

class DAO:
    def __init__(self):
        self.data = []

def App(dao):

    app = Flask("example")

    @app.route("/")
    def m():
        return dao.data

    return app

if __name__ == "__main__":
    app = App(DAO())
    app.run()
使用DI

 

转载于:https://www.cnblogs.com/5poi/p/11263874.html

你可能感兴趣的:(java,python,c/c++)