02.OOP面向对象-1.面向对象介绍

1、面向对象编程介绍

面向对象(object-oriented ;简称: OO) 至今还没有统一的概念 我这里把它定义为: 按人们 认识客观世界的系统思维方式,采用基于对象(实体) 的概念建立模型,模拟客观世界分析、设 计、实现软件的办法。

面向对象编程(Object Oriented Programming-OOP) 是一种解决软件复用的设计和编程方法。 这种方法把软件系统中相近相似的操作逻辑和操作 应用数据、状态,以类的型式描述出来,以对象实例的形式在软件系统中复用,以达到提高软件开发效率的作用。

面向对象的理解:
- 面向对象是一种设计思想
1. 符合人们的思考习惯
2. 把执行者变成指挥者
3. 简化功能,把复杂的事情简单化

- 想完成一个事,找具有这样功能的对象
- 如果能找到,调用这个对象的功能,完成这个事
- 如果找不到,创建具有这样功能的对象,再调用完成这个事
面向对象有三大特征:
1. 封装
2. 继承
3. 多态

2、类和对象

面向对象编程的2个非常重要的概念:类和对象

对象是面向对象编程的核心,在使用对象的过程中,为了将具有共同特征和行为的一组对象抽象定义,提出了另外一个新的概念——类

类就相当于制造飞机时的图纸,用它来进行创建的飞机就相当于对象

- 类是对事务的描述,是抽象的。
- 对象是类的具体体现。
- 类对事务的描述:属性(名词)和行为(动词)

2.1类

具有相同属性和行为事物的统称

2.2对象

某一个具体事物的存在 ,在现实世界中可以是看得见摸得着的

2.3类和对象之间的关系

小总结:类就是创建对象的模板

2.4类的构成

  • 类(Class) 由3个部分构成
  1. 类的名称:类名
  2. 类的属性:一组数据 属性、变量
  3. 类的方法:允许对进行操作的方法 (行为) 方法

2.5类的抽象

拥有相同(或者类似)属性和行为的对象都可以抽像出一个类

3、定义类

类是对事务的描述。
    1、属性
    2、行为

现在呢,要描述汽车这个类。
    1、类名        Car   大驼峰命名法
    2、属性
    3、行为(语法与函数一样,这里叫方法)     run  stop   
        至少有一个参数,名字任意,一般都是self

对象是类的具体体现
创建对象

名字 = 类()
class Car:

    def run(self):
        print('汽车奔跑中。。。。。。')

    def stop(self):
        print('汽车急刹中。。。。。。')


wlhgs = Car()
wlhgs.run()
wlhgs.stop()

bcs = Car()
bm7 = Car()
ad8 = Car()
#每次创建都是新的对象
print(wlhgs,id(wlhgs))
print(bcs,id(bcs))


#虽然每个对象都有类里方法,但是其实是一个方法,地址一样。
print(id(wlhgs.run))
print(id(bcs.run))

4、创建对象

#创建类
class Car:

    def run(self):
        print('汽车奔跑中。。。。。。')

    def stop(self):
        print('汽车急刹中。。。。。。')


def haha(self):
    print('哈哈。。。。。。。')

#创建对象-实例    
bmw = Car()
#调用/执行实例方法
bmw.run()
#为对象实例设置一个属性
bmw.color = '黑色'
#获取对象实例的属性
print('颜色:%s'%(bmw.color))
bmw.brand = '宝马7系'
print('系列:%s'%(bmw.brand))


aodi = Car()
#此时aodi对象实例并没有color属性
#print(aodi.color)

5、__ init__()魔法方法

在上一小节的demo中,我们已经给BMW这个对象添加了2个属性,wheelNum(车的轮胎数量)以及color(车的颜色),试想如果再次创建一个对象的话,肯定也需要进行添加属性,显然这样做很费事,那么有没有办法能够在创建对象的时候,就顺便把车这个对象的属性给设置呢?

  • [x] 答:init()方法

5.1使用方法

在python中类似于这样格式__名字__()的方法叫做魔法方法

__init___
作用:
    在对象创建的时候为对象添加属性--实例属性
特点:
    在创建对象的时候,自动会被调用


self:当前对象实例,哪个对象调用了这个方法,self就是哪个对象

5.2__init__()方法的调用

class Car:
    def __init__(self):
        print('__init__......')
        self.color = '黑色'
        self.brand = '哈弗H6'

    def run(self):
        print('汽车奔跑中。。。。。。%s,%s'%(self,id(self)))

    def stop(self):
        print('汽车急刹中。。。。。。')

# 
#mumaren = Car()
#mumaren.run()
#print('%s,%s'%(mumaren,id(mumaren)))

haval1 = Car()
print(haval1.color)
print(haval1.brand)

haval2 = Car()
print(haval2.color)
print(haval2.brand)

5.3创建对象的时候同时初始化属性

class Car:
    def __init__(self,color,brand):
        print('__init__......')
        self.color = color
        self.brand = brand

    def run(self):
        print('汽车奔跑中。。。。。。%s,%s'%(self,id(self)))

    def stop(self):
        print('汽车急刹中。。。。。。')


haval1 = Car('白色','哈弗h7')
print(haval1.color)
print(haval1.brand)


haval2 = Car('银灰色','哈弗h9')
print(haval2.color)
print(haval2.brand)

#对象是可变类型
haval3 = haval2
haval3.color = '黑色'
print(haval2.color)

5.4总结

  • 当创建Car对象后,在没有调用__init__()方法的前提下,BMW就默认拥有了2个属性wheelNum和color,原因是__init__()方法是在创建对象后,就立刻被默认调用了
  • init()方法,在创建一个对象时默认被调用,不需要手动调用
  • init(self)中,默认有1个参数名字为self,如果在创建对象时传递了2个实参,那么__init__(self)中除了self作为第一个形参外还需要2个形参,例如__init__(self,x,y)
  • init(self)中的self参数,不需要开发者传递,python解释器会自动把当前的对象引用传递进去

6、__ str__()魔法方法

5.1定义__str__()方法

__str__

什么时候调用? 对象转换成字符串   str(对象)  测试的时候,打印对象的信息

格式: 
    def __str__(self):
        return 字符串

print打印的时候,默认就将内容转成字符串

6.2__str__()方法的调用

class Car:
    def __init__(self,color,brand):
        print('__init__......')
        self.color = color
        self.brand = brand
    
    def __str__(self):
        print('__str__......')
        return 'Car color:%s,brand:%s'%(self.color,self.brand)

    def run(self):
        print('汽车奔跑中。。。。。。')

    def stop(self):
        print('汽车急刹中。。。。。。')
    
    def show(self):
        print('color:%s,brand:%s'%(self.color,self.brand))
    

haval1 = Car('白色','哈弗h7')

print(haval1)
print(id(haval1))
#print(str(haval1))
#haval1.show()

6.3总结

  • 在python中方法名如果是__xxxx__()的,那么就有特殊的功能,因此叫做“魔法”方法
  • 当使用print输出对象的时候,只要自己定义了__str__(self)方法,那么就会打印从在这个方法中return的数据(如果__str__没有返回值,报错)

7、理解self

- self表示是当前对象,可以理解为自己
- 可以把self当做C++中类里面的this指针一样理解,就是对象自身的意思
- 某个对象调用其方法时,python解释器会把这个对象作为第一个参数传递给self,所以开发者只需要传递后面的参数即可

8、应用:烤地瓜

8.1分析’烤地瓜‘的属性和方法

示例属性如下:

  • cookedLevel : 这是数字;0~3表示还是生的,超过3表示半生不熟,超过5表示已经烤好了,超过8表示已经烤成木炭了!我们的地瓜开始时时生的
  • cookedString : 这是字符串;描述地瓜的生熟程度
  • condiments : 这是地瓜的配料列表,比如番茄酱、芥末酱等

示例方法如下:

  • cook(): 把地瓜烤一段时间
  • addCondiments(): 给地瓜添加配料
  • init(): 设置默认的属性
  • str(): 让print的结果看起来更好一些

8.2定义类,并且定义__init__()方法

#定义`地瓜`类
class SweetPotato:
    '这是烤地瓜的类'

    #定义初始化方法
    def __init__(self):
        self.cookedLevel = 0
        self.cookedString = "生的"
        self.condiments = []

8.3添加’烤地瓜‘方法

    #烤地瓜方法
    def cook(self, time):
        self.cookedLevel += time
        if self.cookedLevel > 8:
            self.cookedString = "烤成灰了"
        elif self.cookedLevel > 5:
            self.cookedString = "烤好了"    
        elif self.cookedLevel > 3:
            self.cookedString = "半生不熟"
        else:
            self.cookedString = "生的"

8.4基本功能已有,测试

  • 添加以下代码测试
mySweetPotato = SweetPotato()
print(mySweetPotato.cookedLevel)
print(mySweetPotato.cookedString)
print(mySweetPotato.condiments)

8.5测试cook方法是否好用

  • 在上面的代码最后面添加如下代码
print("------接下来要进行烤地瓜了-----")
mySweetPotato.cook(4) #烤4分钟
nt(mySweetPotato.cookedLevel)
print(mySweetPotato.cookedString)

运行结果为:

  • 0
  • 生的
  • []
  • ------接下来要进行烤地瓜了-----
  • 4
  • 半生不熟

8.6定义addCondiments()方法和__str__()方法

def __str__(self):
        msg = self.cookedString + " 地瓜"
        if len(self.condiments) > 0:
            msg = msg + "("
            for temp in self.condiments:
                msg = msg + temp + ", "
            msg = msg.strip(", ")

            msg = msg + ")"
        return msg

def addCondiments(self, condiments):
    self.condiments.append(condiments)

8.7再次测试

定义类:
  属性
    1、cookedLevel           int                 0
    2、cookedString          str                 '生的'
    3、condiments            []                  []
  方法
    1、初始化属性         __init__            cookedLevel,cookedString,condiments
    2、烤地瓜               cook                time
    3、加调料               addCondiment        condiment
    4、地瓜的信息         __str__             

完整的代码如下:

class SweetPotato:
    "这是烤地瓜的类"

    #定义初始化方法
    def __init__(self):
        self.cookedLevel = 0
        self.cookedString = "生的"
        self.condiments = []

    #定制print时的显示内容
    def __str__(self):
        msg = self.cookedString + " 地瓜"
        if len(self.condiments) > 0:
            msg = msg + "("

            for temp in self.condiments:
                msg = msg + temp + ", "
            msg = msg.strip(", ")

            msg = msg + ")"
        return msg

    #烤地瓜方法
    def cook(self, time):
        self.cookedLevel += time
        if self.cookedLevel > 8:
            self.cookedString = "烤成灰了"
        elif self.cookedLevel > 5:
            self.cookedString = "烤好了"    
        elif self.cookedLevel > 3:
            self.cookedString = "半生不熟"
        else:
            self.cookedString = "生的"

    #添加配料
    def addCondiments(self, condiments):
        self.condiments.append(condiments)

# 用来进行测试
mySweetPotato = SweetPotato()
print("------有了一个地瓜,还没有烤-----")
print(mySweetPotato.cookedLevel)
print(mySweetPotato.cookedString)
print(mySweetPotato.condiments)
print("------接下来要进行烤地瓜了-----")
print("------地瓜经烤了4分钟-----")
mySweetPotato.cook(4) #烤4分钟
print(mySweetPotato)
print("------地瓜又经烤了3分钟-----")
mySweetPotato.cook(3) #又烤了3分钟
print(mySweetPotato)
print("------接下来要添加配料-番茄酱------")
mySweetPotato.addCondiments("番茄酱")
print(mySweetPotato)
print("------地瓜又经烤了5分钟-----")
mySweetPotato.cook(5) #又烤了5分钟
print(mySweetPotato)
print("------接下来要添加配料-芥末酱------")
mySweetPotato.addCondiments("芥末酱")
print(mySweetPotato)

运行结果为:

  • ------有了一个地瓜,还没有烤-----
  • 0
  • 生的
  • []
  • ------接下来要进行烤地瓜了-----
  • ------地瓜经烤了4分钟-----
  • ------地瓜又经烤了3分钟-----
  • 半生不熟 地瓜
  • ------接下来要添加配料-番茄酱------
  • 烤好了 地瓜(番茄酱)
  • ------地瓜又经烤了5分钟-----
  • 烤成灰了 地瓜(番茄酱)
  • ------接下来要添加配料-芥末酱------
  • 烤成灰了 地瓜(番茄酱,芥末酱)

9、应用:存放家具

home类:
  属性
    1、面积
  方法
    2、存放家具

bed类:
  属性:
    1、面积
    2、名字
#定义一个home类
class Home:

    def __init__(self, area):
        self.area = area #房间剩余的可用面积
        #self.light = 'on' #灯默认是亮的
        self.containsItem = []

    def __str__(self):
        msg = "当前房间可用面积为:" + str(self.area)
        if len(self.containsItem) > 0:
            msg = msg + " 容纳的物品有: "
            for temp in self.containsItem:
                msg = msg + temp.getName() + ", "
            msg = msg.strip(", ")
        return msg

    #容纳物品
    def accommodateItem(self,item):
        #如果可用面积大于物品的占用面积
        needArea = item.getUsedArea()
        if self.area > needArea:
            self.containsItem.append(item)
            self.area -= needArea
            print("ok:已经存放到房间中")
        else:
            print("err:房间可用面积为:%d,但是当前要存放的物品需要的面积为%d"%(self.area, needArea))


#定义bed类
class Bed:

    def __init__(self,area,name = '床'):
        self.name = name
        self.area = area

    def __str__(self):
        msg = '床的面积为:' + str(self.area)
        return msg

    #获取床的占用面积
    def getUsedArea(self):
        return self.area

    def getName(self):
        return self.name


#创建一个新家对象
newHome = Home(100)#100平米
print(newHome)

#创建一个床对象
newBed = Bed(20)
print(newBed)

#把床安放到家里
newHome.accommodateItem(newBed)
print(newHome)

#创建一个床对象
newBed2 = Bed(30,'席梦思')
print(newBed2)

#把床安放到家里
newHome.accommodateItem(newBed2)
print(newHome)
  • 如果一个对象与另外一个对象有一定的关系,那么一个对象可用是另外一个对象的属性

10、保护对象的属性(私有属性)

如果有一个对象,当需要对其进行修改属性时,有2种方法

对象名.属性名 = 数据 ---->直接修改

对象名.方法名() ---->间接修改

为了更好的保存属性安全,即不能随意修改,一般的处理方式为

将属性定义为私有属性
添加一个可以调用的方法,供调用

私有化某些敏感的数据属性,
对外提供可访问的接口(方法),
这也是一种封装

在java,C#中,要求:

1、所有的属性私有化
2、对外提供get,set

python没要求。

如果某些属性就是不让外部访问,直接__属性名 私有化
是否提供对外访问的接口,根据需要。
class Person:

    def __init__(self):
        self.__age = 18

    def getAge(self):
        #判断一些业务逻辑,如果符号要去,给你数据,不符合,不给。
        return self.__age;

    def setAge(self,age):
        if age<0 or age>120:
            print('年龄不符合要求。。。。。。')
        else:
            self.__age = age


laowang = Person()
print(laowang.getAge())
laowang.setAge(-40)
print(laowang.getAge())

11、__ del__()魔法方法(了解即可)
创建对象后,python解释器默认调用init()方法;

当删除一个对象时,python解释器也会默认调用一个方法,这个方法为__del__()方法

import time

class Animal:
    # 初始化方法
    # 创建完对象后会自动被调用
    def __init__(self, name):
        print('__init__方法被调用')
        self.__name = name

    #对象在被垃圾回收机制的时候调用这个方法,来释放资源。除非有特殊要求,一般不要重写。
    #在关闭数据库连接对象的时候,可以在这里,释放资源
    def __del__(self):
        print('__del__......')

wangcai = Animal('旺财')
xiaoqiang = wangcai

del wangcai
print('*'*50)
del xiaoqiang

time.sleep(10)

print('over......')

12、继承介绍以及单继承

将共性的内容放在父类中,子类只需要关注自己特有的内容
python中所有的内容都是对象,所有的对象都直接或间接继承了object

12.1继承的概念

在程序中,继承描述的是事物之间的所属关系,例如猫和狗都属于动物,程序中便可以描述为猫和狗继承自动物

12.2单继承

将共性的内容放在父类中

子类只关注自己特有的内容

扩展性高,代码更简洁

# 定义一个父类
class Dog(object):
    def __init__(self,name,color):
        self.name = name
        self.color = color

    def run(self):
        print('%s %s run......'%(self.name,self.color))
# 定义一个子类,继承Dog类
class TaiDi(Dog):
    def setName(self,name):
        self.name = name

taidi = TaiDi('泰迪','棕色')
taidi.run() #泰迪 棕色 run......

taidi.setName('泰迪弟')
taidi.run() #泰迪弟 棕色 run......

总结

  • 子类在继承的时候,在定义类时,小括号()中为父类的名字
  • 父类的属性、方法,会被继承给子类

12.3私有的不能被继承

class Animal(object):

    def __init__(self, name='动物', color='白色'):
        self.__name = name
        self.color = color

    def __test(self):
        print(self.__name)
        print(self.color)

    def test(self):
        print(self.__name)
        print(self.color)



class Dog(Animal):
    def dogTest1(self):
        #print(self.__name) #不能访问到父类的私有属性
        print(self.color)


    def dogTest2(self):
        #self.__test() #不能访问父类中的私有方法
        self.test()


A = Animal()
#print(A.__name) #程序出现异常,不能访问私有属性
print(A.color)
#A.__test() #程序出现异常,不能访问私有方法
A.test()

print("------分割线-----")

D = Dog(name = "小花狗", color = "黄色")
D.dogTest1()
D.dogTest2()

总结

  • 私有的属性,不能通过对象直接访问,但是可以通过方法访问
  • 私有的方法,不能通过对象直接访问
  • 私有的属性、方法,不会被子类继承,也不能被访问
  • 一般情况下,私有的属性、方法都是不对外公布的,往往用来做内部的事情,起到安全的作用

13、多继承

多继承:这个类继承了多个父类,是有顺序

继承具有传递性

继承之后,此对象的方法或属性就增加。
调用的时候,注意是否自己有,或者父类有。
class A(object):
    def a(self):
        print('a......')

class B:
    def b(self):
        self.c()  #这里又调用C()中的c
        print('b......')

class C(A,B):
    def c(self):
        print('c......')

# 
#laowang = C()
#laowang.a()
#laowang.b()
#laowang.c()

x = C()
x.b() #c......
      #b......

#获取父类
print(C.__bases__) #(<class '__main__.A'>, <class '__main__.B'>)
print(A.__bases__) #(<class 'object'>,)

#列举,这个类和继承类的结构。调用方法的顺序**(类名.__mro__)**
print(C.__mro__)   #(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)

14、重写

14.1重写父类方法

所谓重写,就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法

class A(object):
    def haha(self):
        print('haha a......')

class B(A):
    def haha(self):
        print('哈哈 b......')
    
b = B()
b.haha()

14.2调用父类的方法

  • 子类覆盖父类,有两种方法可以调用父类的方法
class C2:
    def haha(self):
        print('haha 2......')
    

class C3:
    def haha(self):
        print('haha 3......')


class C4(C3,C2):
    def haha(self):
        #super().haha()
        C2.haha(self)
        print('haha 4......')


laowang= C4()
laowang.haha()
print(C4.__mro__)
class Fu:
    def __init__(self,name,age):
        print('fu...%s'%(id(self)))
        self.name = name
        self.age = age

class Zi(Fu):
    def __init__(self,name,age,sex):
        print('zi...%s'%(id(self)))
        #super().__init__(name,age)   #这是方法一
        Fu.__init__(self,name,age)    #这是方法二
        self.sex = sex
        
    def show(self):
        print('%s,%s,%s'%(self.name,self.age,self.sex))


zi = Zi('老王',43,'男')
zi.show()
print(id(zi))

15、多态

  • 所谓多态:定义时的类型和运行时的类型不一样,此时就成为多态

多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚“鸭子类型”。

首先Python不支持多态,也不用支持多态,python是一种多态语言,崇尚鸭子类型。

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

python既可以说支持多态,也可以说不支持多态


    1、支持多态
        python的弱类型,变量的类型是根据赋值的类型判断的,值是什么类型,变量就是什么类型
        这就是多态

    2、不支持多态
        因为python是弱类型语言,没有要求类型,不完全符合多态的定义。
class F1(object):
    def show(self):
        print('F1.show')

class S1(F1):
    def show(self):
        print('S1.show')

class S2(F1):
    def show(self):
        print('S2.show')

def func(obj):
    obj.show()
    #print(type(obj))


s1_obj = S1()
func(s1_obj) 

s2_obj = S2()
func(s2_obj)

f1 = F1()
func(f1) 

16、类属性、实例属性

  • 直接在类中定义的,与方法平齐,不在方法里的属性就是 类属性
  • 在方法里通过self.属性 都是实例属性

16.1类属性

class People(object):
    name = 'Tom'  #公有的类属性
    __age = 12     #私有的类属性

p = People()

print(p.name)           #正确
print(People.name)      #正确
print(p.__age)         #错误,不能在类外通过实例对象访问私有的类属性
print(People.__age)    #错误,不能在类外通过类对象访问私有的类属性

16.2实例属性(对象属性)

class People(object):
    address = '山东' #类属性
    def __init__(self):
        self.name = 'xiaowang' #实例属性
        self.age = 20 #实例属性

p = People()
p.age =12        #实例属性
print(p.address) #正确
print(p.name)    #正确
print(p.age)     #正确

print(People.address) #正确
print(People.name)    #错误
print(People.age)     #错误

16.3通过实例(对象)去修改类属性

class People(object):
    country = 'china' #类属性


print(People.country)
p = People()
print(p.country)
p.country = 'japan' 
print(p.country)      #实例属性会屏蔽掉同名的类属性
print(People.country)
del p.country    #删除实例属性
print(p.country)

总结

  • 如果需要在类外修改类属性,必须通过类对象去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的实例属性,这种方式修改的是实例属性,不会影响到类属性,并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性,除非删除了该实例属性。

17、实例方法、类方法和静态方法

17.1实例方法

实例方法/对象方法,有一个参数,一般叫self
在使用的时候,对象.方法名(实参)
self不需要手动传值,默认将当前对象传递过去给self

17.2类方法

语法:

@classmethod
def 方法名(cls,x,y,z...):
    语句


调用:
    对象名.
    类名.

cls:类对象


类也是对象,type的对象,

在类方法中,设置的属性,是类属性,所有对象共享。



什么时候用类方法呢?
    1、想通过类直接访问的方法
    2、想通过方法来设置或者修改类属性
class People(object):
    country = 'china'

    #类方法,用classmethod来进行修饰
    @classmethod
    def getCountry(cls):
        return cls.country

    @classmethod
    def setCountry(cls,country):
        cls.country = country


p = People()

print(p.getCountry())     #china    #可以用过实例对象引用
print(People.getCountry())#china        #可以通过类对象引用

p.setCountry('japan')   

print(p.getCountry())     #japan

17.3静态方法

语法:
    @staticmethod
    def 名(形参):
        语句

    实例方法必须至少有一个参数放在第一个,一般叫self
    类方法必须至少有一个参数放在第一个,一般叫cls

    静态方法可以没有参数,也可以有参数,但是无法直接获取类对象和实例对象
    所以一般静态方法里的功能,不与对象相关
class Dog:
    age = 18

    def __init__(self,name):
        self.name = name

    @staticmethod
    def show():
        print('show.....%s'%(Dog.age))

d = Dog('旺财')
d.show()   #show.....18

- 静态方法中不需要额外定义参数
- 因此在静态方法中引用类属性的话,必须通过类对象来引用

18、设计模式

18.1设计模式的六大原则

  1. 设计模式六大原则(1):单一职责原则

    即一个类只负责一项职责
  2. 设计模式六大原则(2):里氏替换原则

    所有引用基类的地方必须能透明地使用其子类的对象
  3. 设计模式六大原则(3):依赖倒置原则

    高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
  4. 设计模式六大原则(4):接口隔离原则

    客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
  5. 设计模式六大原则(5):迪米特法则

    一个对象应该对其他对象保持最少的了解。尽量降低类与类之间的耦合。
  6. 设计模式六大原则(6):开闭原则

    一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。

18.2分类

1.创建型模式

主要目的:创建对象
共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

2.结构型模式

主要目的:对象的组成和对象的关系
共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

3.行为型模式

主要目的:对象的行为,对象能做什么
共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

19、工厂设计模式

组成:

  1. 工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑,用来创建产品
  2. 抽象产品角色:它一般是具体产品继承的父类或者实现的接口。
  3. 具体产品角色:工厂类所创建的对象就是此角色的实例。

19.1简单工厂模式

实例:

'''
    抽象角色(父类):它一般是具体产品继承的父类或者实现的接口
'''
class Car(object):
    def move(self):
        pass
    def stop(self):
        pass

'''
    具体产品角色(继承抽象角色):工厂类所创建的对象就是此角色的实例。
'''
class H1(Car):
    def move(self):
        print('哈弗h1 move......')
        
    def stop(self):
        print('哈弗h1 stop......')


class H9(Car):
    def move(self):
        print('哈弗h9 move......')
        
    def stop(self):
        print('哈弗h9 stop......')

class H7(Car):
    def move(self):
        print('哈弗h7 move......')
        
    def stop(self):
        print('哈弗h7 stop......')

class H6(Car):
    def move(self):
        print('哈弗h6 move......')
        
    def stop(self):
        print('哈弗h6 stop......')


'''
    工厂类角色:这是本模式的核心,含有一定的逻辑判断,用来创建产品
'''
class CarFactory:
    @classmethod
    def createCar(cls,name):
        car = None
        if name=='哈弗H1':
            car = H1()
        elif name=='哈弗H9':
            car = H9()
        elif name=='哈弗H7':
            car = H7()
        elif name=='哈弗H6':
            car = H6()
        return car

'''
    主代码
'''
name = input('请输入要购买的车型:')
car = CarFactory.createCar(name)
if car!=None:
    car.move()
    car.stop()
else:
    print('没有。。。。。。。')

说明:
工厂函数、工厂类对具体的生成环节进行了封装,这样有利于代码的后需扩展,即把功能划分的更具体,4s店只负责销售,汽车厂只负责制造

总结:
对象创建比较复杂的时候,可以考虑使用简单工厂

  1. 优点:
    在简单工厂中主函数或者客户端不再负责对象的创建,而是把这个责任交给工厂类,主函数或者客户端在使用对象的时候只从工厂中调用就行了,从而明确了各个类的职责,符合单一职责原则)
  2. 缺点:
    由于这个工厂类负责所有对象的创建,那么当子类增多时,我们就需要去修改工厂类的代码,这样呢,就违反了一个原则:开闭原则

19.2工厂方法模式

'''
    抽象角色(父类):它一般是具体产品继承的父类或者实现的接口
'''
class Car(object):
    def move(self):
        pass
    def stop(self):
        pass

'''
    具体产品角色(继承抽象角色):工厂类所创建的对象就是此角色的实例。
'''
class H1(Car):
    def move(self):
        print('哈弗h1 move......')
        
    def stop(self):
        print('哈弗h1 stop......')


class H9(Car):
    def move(self):
        print('哈弗h9 move......')
        
    def stop(self):
        print('哈弗h9 stop......')

class H7(Car):
    def move(self):
        print('哈弗h7 move......')
        
    def stop(self):
        print('哈弗h7 stop......')

'''
    抽象工厂类角色
'''

class CarFactory:
    @classmethod
    def createCar(cls,name):
        pass
    

'''
    具体工厂类角色:这是本模式的核心,含有一定的逻辑判断,用来创建产品
'''
class H1Factory(CarFactory):
    @classmethod
    def createCar(cls,name):    
        return H1()

class H9Factory(CarFactory):
    @classmethod
    def createCar(cls,name):    
        return H9() 

class H7Factory(CarFactory):
    @classmethod
    def createCar(cls,name):    
        return H7() 
'''
    主代码
'''
name = input('请输入要购买的车型:')
car = None

if name=='哈弗H1':
    car = H1Factory.createCar(name)
elif name=='哈弗H9':
    car = H9Factory.createCar(name)

if car!=None:
    car.move()
    car.stop()
else:
    print('没有。。。。。。。')

总结:
工厂方法模式的优点和缺点

  1. 优点:
    解决了简单工厂模式的违反开闭原则
  2. 缺点:
    1如果需要增加一个具体产品类角色,需要添加这个类和对应的工厂类。代码量大。

20、__new__魔法方法的使用

a1 = A()

创建对象的步骤
1、首先调用__new__得到一个对象
2、调用__init__为对象添加属性
3、将对象赋值给变量
class A(object):
    def __init__(self):
        print("这是 init 方法")

    def __new__(cls):
        print(id(cls))
        print("这是 new 方法")  
        return object.__new__(cls)
    
    
a1 = A()
print(a1)
#7214424
#这是 new 方法
#这是 init 方法
#<__main__.A object at 0x0000000000BDEB00>
print(id(a1)) #12446464
print(id(A))  #7214424

总结

  • __new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供
  • __new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类__new__出来的实例,或者直接是object的__new__出来的实例
  • __init__有一个参数self,就是这个__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值
  • 我们可以将类比作制造商,__new__方法就是前期的原材料购买环节,__init__方法就是在有原材料的基础上,加工,初始化商品环节

21、单例设计模式(单列模式Singleton)

21.1单例是什么

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,单例模式是一种对象创建型模式。

21.2创建单例-保证只有1个对象

1、单例模式Singleton
        在项目中某些对象,只有一个,可以一直使用。
        如果再次实例化,会很占资源和时间。
        所以,这样的对象就需要设计成单例模式。

2、原型模式Prototype
        可以实例化多个对象,每个都是新的,以前设计的类都是原型模式


object.__new__(cls):
        表示创建了一个实例对象
class Singleton:
    #表示对象是否被创建 None:没有,其它:已经创建
    __instance = None
    def __new__(cls):
        if cls.__instance == None:
            cls.__instance = object.__new__(cls)

        return cls.__instance



s1 = Singleton()
s2 = Singleton()
s3 = Singleton()
print(id(s1))   #18342968
print(id(s2))   #18342968
print(id(s3))   #18342968

21.3不定长参数

class Singleton:
    #表示对象是否被创建 None:没有,其它:已经创建
    __instance = None
    #表示是不是第一次调用init:        True:第一次调用  False:不是第一次调用
    __firstInit = True
    def __init__(self,*args,**kwargs):
        if Singleton.__firstInit:
            self.args = args
            self.kwargs = kwargs
            Singleton.__firstInit=False
    def __new__(cls,*args,**kwargs):
        if cls.__instance == None:
            cls.__instance = object.__new__(cls)

        return cls.__instance



s1 = Singleton('老王',10,'add','',money=12345)
print(id(s1))       #8514472
print(s1.args[0])   #老王
print(s1.args[2])   #add
print(s1.kwargs['money'])#12345

s2 = Singleton('旺财')
print(id(s2))       #8514472
print(s2.args[0])   #老王
print(s2.args[2])   #add
print(s2.kwargs['money'])#12345

s3 = Singleton()
print(s3.kwargs['money'])#12345

猜你喜欢

转载自www.cnblogs.com/cjr0707/p/9694584.html