面向对象之组合与封装

一  组合

解决类与类之间代码的冗余问题有两种方式:

    1)继承:继承是类与类之间什么是什么的关系,是一种从属关系,子类从属于父类。

    2)组合:类与类之间的关系,是一种什么有什么的关系,一个类产生的对象,该对象有一个属性,这个属性的值来自另一个对象。也即是说在一个类中以另一个类的对象作为数据属性,称为类的组合。

下面我们用一个实例来说明下:

class Equip: #武器装备类
...     def fire(self):
...         print('release Fire skill')
... 
>>> class Riven: #英雄Riven的类,一个英雄需要有装备,因而需要组合Equip类
...     camp='Noxus'
...     def __init__(self,nickname):
...         self.nickname=nickname
...         self.equip=Equip() #用Equip类产生一个装备,赋值给实例的equip属性
... 
>>> r1=Riven('锐雯雯')
>>> r1.equip.fire() #可以使用组合的类产生的对象所持有的方法
release Fire skill
View Code
class People:
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex

class Course:
    def __init__(self,name,period,price):
        self.name=name
        self.period=period
        self.price=price
    def tell_info(self):
        print('<%s %s %s>' %(self.name,self.period,self.price))

class Teacher(People):
    def __init__(self,name,age,sex,job_title):
        People.__init__(self,name,age,sex)
        self.job_title=job_title
        self.course=[]
        self.students=[]


class Student(People):
    def __init__(self,name,age,sex):
        People.__init__(self,name,age,sex)
        self.course=[]


egon=Teacher('egon',18,'male','沙河霸道金牌讲师')
s1=Student('牛榴弹',18,'female')

python=Course('python','3mons',3000.0)
linux=Course('python','3mons',3000.0)

#为老师egon和学生s1添加课程
egon.course.append(python)
egon.course.append(linux)
s1.course.append(python)

#为老师egon添加学生s1
egon.students.append(s1)


#使用
for obj in egon.course:
    obj.tell_info()
View Code

当类与类之间显著不同的时候,我们可以可以用组合的方式,减少代码的冗余。

二  封装

我们从字面上来理解封装:就是用一个袋子把一些东西装进去然后把袋子系起来,这样就做到了封装。

我们可以把封装看成一个简单的隐藏,只是改变了隐藏对象的外形,其实实质没变。

其实这仅仅这是一种变形操作且仅仅只在类定义阶段发生变形
类中所有双下划线开头的名称如__x都会在类定义时自动变形成:_类名__x的形式

class A:
    __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
    def __init__(self):
        self.__X=10 #变形为self._A__X
    def __foo(self): #变形为_A__foo
        print('from A')
    def bar(self):
        self.__foo() #只有在类内部才可以通过__foo的形式访问到.

如果我们一定要从外部访问也是可以访问到的,使用A._A__N是可以访问到的,这种,在外部是无法通过__x这个名字访问到。

那么这种变形我们要注意三个问题:

    1)这种封装并不能真正的限制我们从外部直接访问,只是要改变访问方式:_类名__属性,然后就可以访问到这个属性。

    2)这种语法意义上的变形,只在类定义阶段发生一次,类定义之后,新增的以__开头的属性都没有变形的效果。

   3)如果父类不想让子类覆盖自己的方法,可以在方法前加__开头。

封装数据:

我们上面说封装是一种隐藏,其实严格意义上他不算隐藏,封装的目的是用来被调用的,所以还是要被外部调用,但是只是换了一种方式,不是直接调用而是在调用的过程加上一些限制条件,来完成对于数据属性操作的严格控制。

class Teacher:
    def __init__(self,name,age):
        # self.__name=name
        # self.__age=age
        self.set_info(name,age)

    def tell_info(self):
        print('姓名:%s,年龄:%s' %(self.__name,self.__age))
    def set_info(self,name,age):
        if not isinstance(name,str):
            raise TypeError('姓名必须是字符串类型')
        if not isinstance(age,int):
            raise TypeError('年龄必须是整型')
        self.__name=name
        self.__age=age


t=Teacher('egon',18)
t.tell_info()

t.set_info('egon',19)
t.tell_info()

封装方法:(封装函数属性)

比如我们的相机,为我们提供了拍照功能,其实在拍照的过程中,相机会自动调节一些饱和度,曝光度等,只是我们将这些功能封装起来而已。

提示:在编程语言里,对外提供的接口(接口可理解为了一个入口),可以是函数,称为接口函数,这与接口的概念还不一样,接口代表一组接口函数的集合体。

取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做
隔离了复杂度,同时也提升了安全性

class ATM:
    def __card(self):
        print('插卡')
    def __auth(self):
        print('用户认证')
    def __input(self):
        print('输入取款金额')
    def __print_bill(self):
        print('打印账单')
    def __take_money(self):
        print('取款')

    def withdraw(self):
        self.__card()
        self.__auth()
        self.__input()
        self.__print_bill()
        self.__take_money()

a=ATM()
a.withdraw()

 

原文地址:https://www.cnblogs.com/zhangsanfeng/p/8824624.html