zoukankan      html  css  js  c++  java
  • 如何理解“面向对象”编程思想

    理解面向对象,首先理解要它的基础概念:

    面向对象 ( Object Oriented ) 是将现实问题构建关系,然后抽象成 类 ( class ),给类定义属性和方法后,再将类实例化成 实例 ( instance ) ,通过访问实例的属性和调用方法来进行使用。

    在不同的语言中,对象的定义范围不同。在 Python 中“类”和“类的实例”都称为 对象 ( Object ),因为 Python 的类是更顶级的 type 实例化后的对象,也就是常说的“Python 里万物皆对象”;而在 Java 等静态语言中,一般把类的实例称为对象。

    理解了理论知识后,接着通过例子,再理解面向对象的三大特征:封装、继承、多态。

    下边我们把“女娲造人”这个神话故事,用 Python 的面向对象代码来叙述一遍:

    假设我们是女娲(程序设计者),我们突然有个想法,想造一群和自己差不多的小人,小人需要有男女两种性别,外观和行为也有一些差异。那首先我们分析出,不管什么性别,都应该有四肢,所以我们先仿照自己的构造,在脑海中构思泥人的样子(抽象成基类),然后先赋予泥人一些共有的行为(定义类的实例方法):

    class Human(object):
    
        def __init__(self, name):
            # 有个名字,有两只手,两条腿
            self._name = name
            self.hands = 2
            self.legs = 2
    
        def introduce_self(self):
            # 介绍自己
            print('我是%s' % self._name)
    
        def work(self):
            # 工作,但还没有定义具体的行为
            raise NotImplementedError
    

    然后我们先捏3个泥人(实例化对象),并给他们取了不同的名字(初始化实例属性):

    >>> a = Human('大强子')
    >>> b = Human('二狗子')
    >>> c = Human('三愣子')
    

    我们让其中一个人介绍自己(调用实例方法):

    >>> a.introduce_self()
    我是大强子
    

    这里解释一下 Human 的代码,虽然设定了每个人都要工作,但如何工作需要到具体到不同类型的人,所以在基类里我们并没有定义 work 方法的内容,如果强行调用会抛出异常。

    还有一点,上面定义属性时,我们把 self._name 前边加了下划线,是因为 Python 里用下划线来约定这是一个受保护变量(对应 Java 中的 protected ),我们不希望外界能直接访问 name 这个属性,必须要通过对象调用 introduce_self() 这个行为介绍了自己,别人才能知道他叫什么名字,这个过程就称之为封装。

    然后我们继续完成想法,需要给泥人增加两种性别,并且异性之间能结婚,我们开始在刚才泥人模型的基础上(继承于基类),构思出两种性别的泥人的区别(设置不同的属性),然后让他们都可以工作,但工作的内容不一样(调用相同的方法出现不同结果,是多态性),并决定让男人可以娶女人(将这个行为定义为男人的方法)。

    import random
    
    class Female(Human):
    
        def __init__(self, name):
            # 调用父类的初始化方法,依然有名字、两只手、两条腿
            super().__init__(name)
            # 头发和力量进行随机取值
            self.hair = random.randint(3, 5)
            self.power = random.randint(1, 3)
            # 是否已婚
            self.married = False
    
        def work():
            print('%s采摘了一些果子' % self.name)
    
    class Male(Human):
    
        def __init__(self, name):
            super().__init__(name)
            self.hair = random.randint(0, 2)
            self.power = random.randint(2, 5)
            self.married = False
    
        def work():
            print('%s出去打猎了一天' % self.name)
    
        def marry(self, other):
            # 判断自己或对方是否已结婚,否则抛出异常
            if self.married is True or other.married is True:
                raise ValueError('法律不支持多次结婚')
            # 判断对方是否是女性,否则抛出异常
            if isinstance(other, Female):
                self.married = True
                other.married = True
            else:
                raise TypeError('法律不支持同性结婚')
    

    然后我们就可以让小人活动起来:

    >>> a = Male('大强子')
    >>> b = Male('二狗子')
    >>> c = Female('翠花')
    >>> for h in [a, b, c]:
    ...     # 调用父类的方法
    ...     h.introduce_self()
    我是大强子
    我是二狗子
    我是翠花
    >>> for h in [a, b, c]:
    ...     # 多态性使相同的方法产生不同的结果
    ...     h.work()
    大强子出去打猎了一天
    二狗子出去打猎了一天
    翠花采摘了一些果子
    >>> a.marry(c)
    >>> a.married
    True
    >>> c.married
    True
    >>> b.marry(c)
    ValueError: 法律不支持多次结婚
    >>> b.marry(a)
    TypeError: 法律不支持同性结婚
    

    设计到此结束,我们来复盘一下整个过程。

    我们先是把人的共有特征抽象成 Human 基类,这个基类并不用于实例化,而是用于让 Female 和 Male 继承它,并实现不同的行为。这样我们就避免把一些共有的行为重复在多个类里定义,如果我们后续想对人类的行为进行变动,也只需要修改 Human,继承 Human 的子类会自动获得新行为,这是 继承带来的好处。

    我们把 name 设计为受保护变量,外界无法直接访问这个属性,让每个人的隐私得到了保障(一些不必要的行为变得可控),这是 封装 带来的好处。

    同时我们在 Human 中预留了 work 方法,并在 Female 和 Male 都实现了不同的效果,然后我们知道人人都有 work 方法,因此可以像 introduce_self 一样,用循环批量调用 work 方法,这是 多态 带来的好处。

    看到这里你应该有些理解:面向对象是将客观事物和一些关系,抽象成具体的模型(类),并为其设计属性和方法,即 对象 = 属性(特征)+ 方法(行为)。

    如果是拥有复杂关系的需求,我们就应该尽可能将互相有关联的行为抽象成类,比如每一个网页,网页中每一个组件 等等。实际上面向对象帮助我们在几万行代码的大型项目中,仍然可以游刃有余,正因为如此,才能发展为目前应用最为广泛的编程思想。

    但也并不是说任何时候都要“面向对象”,过度的封装和抽象,也会造成代码可读性的下降,以及运行效率的下降,因此我们应该在能将事物抽象化的需求中使用面向对象。


    最后,不管是面向什么编程,终究还是要面向人生

    欢迎关注我的微信公众号:面向人生编程

    回复【资料】获取本人精选的学习视频及代码

  • 相关阅读:
    导包路径
    django导入环境变量 Please specify Django project root directory
    替换django的user模型,mysql迁移表报错 django.db.migrations.exceptions.InconsistentMigrationHistory: Migration admin.0001_initial is applied before its dependen cy user.0001_initial on database 'default'.
    解决Chrome调试(debugger)
    check the manual that corresponds to your MySQL server version for the right syntax to use near 'order) values ('徐小波','XuXiaoB','男','1',' at line 1")
    MySQL命令(其三)
    MySQL操作命令(其二)
    MySQL命令(其一)
    [POJ2559]Largest Rectangle in a Histogram (栈)
    [HDU4864]Task (贪心)
  • 原文地址:https://www.cnblogs.com/zkqiang/p/10515187.html
Copyright © 2011-2022 走看看