继承与合成

Posted masterpaopao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了继承与合成相关的知识,希望对你有一定的参考价值。

在阅读本文之前,你只需要记住我这一句:

大部分使用继承的场合后可以用合成来取代,而多重继承则需要不惜一切地避免之。

首先我详细介绍一下继承的概念:

‘‘‘
什么是继承?
继承就是用来指明一个类的大部分或全部功能都是从一个父类中获得的。
比如class Foo(Bar):创建一个叫Foo的类,并让它继承Bar。
当你这样写的时候,Bar的实例所具有的功能都工作在Foo的实例上。
当你这么做的时候,父类和子类有三种交互方式:
1.子类上的动作完全等同于父类上的动作。
2.子类上的动作完全覆盖了父类上的动作。
3.子类上的动作部分替换了父类上的动作。
‘‘‘
‘‘‘
①隐式继承:
当你在父类里定义了一个函数但没有在子类中定义的例子时,会发生隐式继承。
‘‘‘
class Parent(object):
     def implicit(self):
         print("PARENT implicit()")
 class Child(Parent):   
     pass            #类Child里面没有定义任何细节,但是从Parent父类继承所有的行为。
 
dad=Parent()
son=Child()
dad.implicit()
son.implicit()
‘‘‘
我们可以看见,输出结果都是:PARENT implicit()。
说明了子类Child继承了父类Parent的所有功能,这就是隐式继承。
方法是创建一个子类的时候,括号内填入父类函数,不定义任何东西,就能完成继承父类的功能。
优点:需要很多类时,这样可以避免重复写很多代码。
‘‘‘
 
‘‘‘
②显式覆盖:
有时候,需要让子类的函数有一个不同的行为,显然这是隐式继承做不到的差事。
在子类中Child定义一个相同名称的函数就可以了。
‘‘‘
class Parent(object):
   def override(self):
        print("PARENT override()")
class Child(Parent):
    def override(self):
        print("CHILD pverride()")
 
dad=Parent()
son=Child()
dad.override()
son.override()
‘‘‘
从输出结果来看:
PARENT override()
CHILD pverride()
虽然子类Child继承了父类Parent的功能,但并不代表输出结果就应该一样。
dad.override()的打印结果应该没问题,这是Parent父类的一个实例。
son.override(),son是从子类Child的一个实例,虽然它继承了父类Parenet的功能,
但是类Child的实例是由类Child下定义的函数来执行,虽然函数名一样,但是函数内的变量属于局部变量大家应该知道。
当类Child下没有定义任何的函数时,这时候才去继承父类Parenet里面的函数来执行,这个过程就叫隐式继承。
大家弄清楚隐式继承和显式覆盖的区别了吗?
‘‘‘
 
‘‘‘
③在运行前或运行后替换:
这是使用继承的第三种方法。这种情况下,你想在父类中定义的内容运行之前或者之后在修改行为。
‘‘‘
class Parent(object):
    def altered(self):
        print("PARENT altered()")
class Child(Parent):
    def altered(self):
        print("CHILD,BEFORE PARENT altered()")
        super(Child,self).altered()
        print("CHILD,AFTER PARENT altered()")
 
dad=Parent()
son=Child()
dad.altered()
son.altered()
‘‘‘
我们先来看一下输出的结果:
PARENT altered()
CHILD,BEFORE PARENT altered()
PARENT altered()
CHILD,AFTER PARENT altered()
第一行、第二行和第四行的输出结果应该没有什么疑问,现在重点分析第三行的输出结果。
super(Child,self).altered(),这一行调用了父类的函数,我们先分析以下super()的作用:
super() 函数是用于调用父类(超类)的一个方法,这个方法是用来解决多重继承的问题,直接调用父类。
那么我们可以把这句话解读为:
用Child和self这两个参数调用super,找到Child的父类Parent,把子类Child实例的对象转换为父类Parent的实例对象,
然后在此返回的基础上调用altered函数。到这里Parent.altered就会被打印,从而打印出父类里的信息。
我这么解释是不是比较通俗易懂?希望对你有帮助!
‘‘‘

‘‘‘

④为什么要用super():

其实我在上面已经隐隐约约地给出了答案,就是要应对一个叫“多重继承”的麻烦东西。

多重继承是指定义的类继承了多个类,就像这样:

class SuperFun(Child,BadStuff):

    pass

也就是说,创建一个叫SuperFun的类,让它同时继承Child和BadStuff。

这样一来一旦在SuperFun的实例上调用任何隐式动作,Python就要回到类的层次结构中以固定的次序去检查,这样会很麻烦吧。

所以super()就在这个形势下诞生了,再也不用担心把继承关系弄糟,因为会正确快速地找到需要的函数。

super()和__init__搭配使用:

这是最常见的super()与基类的__init__函数搭配使用,在子类里做了一些事情,然后完成对父类的初始化。

class Child(Parent):

    def __init__(self,stuff):

        self.stuff=stuff

        super(Child,self).__init__()

‘‘‘

‘‘‘

⑤合成:

继承虽然是一种有用的技术,不过还有一种实现相同功能的方法,就是直接使用别的类和模块,而非依赖于继承。

‘‘‘

class Other(object):

    def override(self):

        print("OTHER override()")

    def implicit(self):

        print("OTHER implicit()")

    def altered(self):

        print("OTHER altered()")

class Child(object):

    def __init__(self):

        self.other=Other()

    def implicit(self):

        self.other.implicit()

    def override(self):

        print("CHILD override()")

    def altered(self):

        print("CHILD,BEFORE OTHER altered()")

        self.other.altered()

        print("CHILD,AFTER OTHER altered()")

 

son=Child()

son.implicit()

son.override()

son.altered()

‘‘‘

先来看看输出结果吧:

OTHER implicit()

CHILD override()

CHILD,BEFORE OTHER altered()

OTHER altered()

CHILD,AFTER OTHER altered()

开始的时候,你可能看不懂输出结果,我现在来给你整理一下思路。

很明显,类Other和类Child并没有父类子类的关系,而是一个平等的关系。

但是我们可以看到类Child的__init__函数中有类Other,这就是“A里有B”的关系。

这些过程就是一个引用的过程,比如son.implicit():

son是类Child的一个实例,然后调用son.other.implicit(),

在__init__中,son.other是等于Other(),所以调用的这一句是等同于Other().impicit(),

所以输出结果自然就成了类Other里的implicit()函数的打印信息。

这种方式就叫做合成,不需要继承关系,而是选择了“A里面有B”的引用方式,能有效避免多种继承。

‘‘‘

‘‘‘

⑥继承和合成的应用场合:

“继承和合成”的问题说到底还是为了解决关于代码复用的问题,什么时候用哪一种方式取决于你的需求。

继承可以让你在基类里隐含父类的功能,从而解决这个问题。

合成则是利用模块和别的类中的函数调用达到了相同的目的。

以下是一些老程序员的建议:

1.不惜一切代价地避免多重继承,因为它带来的麻烦比能解决的问题都多。如果非要用,

那得准备好专研类的层次结构,以及花时间去找各种东西的来龙去脉。

2.如果有一些代码会在不同位置和场合应用到,那就用合成来把它们做成模块。

3.只有在代码之间有清楚的关联,可以通过一个单独的共性联系起来的时候使用继承,

或者受现有代码或者别的不可抗拒因素所限非用不可,那也用吧。

‘‘‘

 

 

希望你已经理解了继承与合成,与此同时,我要恭喜你,你已经有了自食其力的能力了。

现在你需要制作一个游戏。在这儿,我有一些建议要告诉你:

①函数的风格:

学会合理使用继承与合成;

让函数保持简洁小巧;

不要让某一个函数做太多的事情。

②类的风格:

类的命名使用"驼峰式大小写",例如SuperMan,ComeOn等等;

__init__不应该做太多的事情;

类下面定义的函数名称不要用驼峰式大小写,用别的;

用一致的方式组织函数的参数,如果类处理users,dogs和cats,请在其它地方也保持这个次序。

③代码的风格:

合理使用空行;

每行不超过80个字符;

让自己的代码整体看起来简洁美观一点;

④好的注释:

养成注释的好习惯;

写注释的时候描述清楚为什么要这样做;

让注释尽量看起来短小精悍;

⑤客观评价你的代码;

主动反思代码有哪些不能实现的功能;

当你不能很好地实现可能存在的情况,请在注释里写出来。

 

祝你能顺利写出一个好游戏!

‘‘‘
①隐式继承:
当你在父类里定义了一个函数但没有在子类中定义的例子时,会发生隐式继承。
‘‘‘
# class Parent(object):
# def implicit(self):
# print("PARENT implicit()")
# class Child(Parent):
# pass #类Child里面没有定义任何细节,但是从Parent父类继承所有的行为。

# dad=Parent()
# son=Child()
# dad.implicit()
# son.implicit()
‘‘‘
我们可以看见,输出结果都是:PARENT implicit()。
说明了子类Child继承了父类Parent的所有功能,这就是隐式继承。
方法是创建一个子类的时候,括号内填入父类函数,不定义任何东西,就能完成继承父类的功能。
优点:需要很多类时,这样可以避免重复写很多代码。
‘‘‘

‘‘‘
②显式覆盖:
有时候,需要让子类的函数有一个不同的行为,显然这是隐式继承做不到的差事。
在子类中Child定义一个相同名称的函数就可以了。
‘‘‘
# class Parent(object):
# def override(self):
# print("PARENT override()")
# class Child(Parent):
# def override(self):
# print("CHILD pverride()")

# dad=Parent()
# son=Child()
# dad.override()
# son.override()
‘‘‘
从输出结果来看:
PARENT override()
CHILD pverride()
虽然子类Child继承了父类Parent的功能,但并不代表输出结果就应该一样。
dad.override()的打印结果应该没问题,这是Parent父类的一个实例。
son.override(),son是从子类Child的一个实例,虽然它继承了父类Parenet的功能,
但是类Child的实例是由类Child下定义的函数来执行,虽然函数名一样,但是函数内的变量属于局部变量大家应该知道。
当类Child下没有定义任何的函数时,这时候才去继承父类Parenet里面的函数来执行,这个过程就叫隐式继承。
大家弄清楚隐式继承和显式覆盖的区别了吗?
‘‘‘

‘‘‘
③在运行前或运行后替换:
这是使用继承的第三种方法。这种情况下,你想在父类中定义的内容运行之前或者之后在修改行为。
‘‘‘
# class Parent(object):
# def altered(self):
# print("PARENT altered()")
# class Child(Parent):
# def altered(self):
# print("CHILD,BEFORE PARENT altered()")
# super(Child,self).altered()
# print("CHILD,AFTER PARENT altered()")

# dad=Parent()
# son=Child()
# dad.altered()
# son.altered()
‘‘‘
我们先来看一下输出的结果:
PARENT altered()
CHILD,BEFORE PARENT altered()
PARENT altered()
CHILD,AFTER PARENT altered()
第一行、第二行和第四行的输出结果应该没有什么疑问,现在重点分析第三行的输出结果。
super(Child,self).altered(),这一行调用了父类的函数,我们先分析以下super()的作用:
super() 函数是用于调用父类(超类)的一个方法,这个方法是用来解决多重继承的问题,直接调用父类。
那么我们可以把这句话解读为:
用Child和self这两个参数调用super,找到Child的父类Parent,把子类Child实例的对象转换为父类Parent的实例对象,
然后在此返回的基础上调用altered函数。到这里Parent.altered就会被打印,从而打印出父类里的信息。
我这么解释是不是比较通俗易懂?希望对你有帮助!
‘‘‘





以上是关于继承与合成的主要内容,如果未能解决你的问题,请参考以下文章

go语言设计模式-合成复用原则

设计模式软件设计七大原则 ( 合成复用原则 | 代码示例 )

自动属性合成 (@property) 和继承

合成聚合原则

合成/聚合复用原则,桥接模式

微服务 继承与构成:如何选择