文章目录
- 一、引言
- 二、为什么学习面向对象编程
- 2.1 提高代码的可维护性:通过封装、继承和多态实现模块化设计
- 2.2 提升代码的复用性:通过类和对象的创建实现代码的重用
- 三、类和对象的基本概念
- 3.1 类和对象的定义和关系:类是对象的模板,对象是类的实例
- 3.2 属性和方法:类和对象具有的特征和行为
- 3.3 初始化方法:__init__()方法的作用和使用
- 四、类的继承与多态
- 4.1 继承的概念和分类:单继承和多继承
- 4.2 多态的实现:不同对象对相同方法的不同表现
- 五、封装和访问控制
- 5.1 封装的概念和作用:将数据和操作封装在类中
- 5.2 公有、私有和受保护的访问控制:属性和方法的可见性和访问限制
- 六、魔法方法和特殊属性
- 6.1 魔法方法的介绍和使用:__repr__()、__str__()等
- 6.2 特殊属性的应用:__dict__、__name__等
- 七、类的设计原则和模式
- 7.1 SOLID原则:单一职责、开闭原则、里式替换、接口隔离原则
- 7.2 常见设计模式:工厂模式、单例模式、观察者模式等
- 八、实践项目:创建一个简单的图书管理系统
重温Python,适合新手搭建知识体系,也适合大佬的温故知新~
一、引言
面向对象编程为Python开发提供了一种有效的编程范式,可以提高代码的可重用性、简化复杂性、增强扩展性、保护数据安全和提高代码的可维护性。
面向对象编程对Python的重要性:
- 可重用性:面向对象编程通过封装数据和行为在一个对象中,使得代码更加模块化和可重用。通过定义类和创建对象,可以在不同的项目中重复使用代码,提高开发效率。
- 简化复杂性:面向对象编程通过抽象、继承和多态等特性,可以将复杂的问题分解成简单的对象,并通过对象之间的交互解决问题。这样可以简化代码的设计和实现过程,提高代码的可读性和维护性。
- 扩展性:面向对象编程允许通过继承和多态来扩展已有的类,从而实现新的功能。这种灵活性使得代码可以随着需求的变化而进行扩展和修改,同时保持原有代码的稳定性。
- 封装性:面向对象编程通过将数据和行为封装在一个对象中,实现了数据的隐藏和保护。这样可以防止外部直接访问和修改对象的内部状态,提高代码的安全性和稳定性。
- 可维护性:面向对象编程使得代码更加结构化和模块化,易于理解和维护。通过将功能相似的代码封装在类中,可以更好地组织代码,减少了代码的冗余和重复,使得代码更加可读和易于修改。
二、为什么学习面向对象编程
2.1 提高代码的可维护性:通过封装、继承和多态实现模块化设计
通过封装、继承和多态的应用,可以将代码模块化,降低代码的耦合度,增加代码的可读性、可维护性和可扩展性 ,可以提高代码的可维护性,并实现模块化的设计。
封装:
封装是指将数据和对数据的操作封装在一起,以形成一个独立的单元。通过封装,我们可以隐藏内部实现细节,只暴露必要的接口给其他部分使用。这样可以增强代码的安全性和可维护性。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def get_name(self):
return self.name
def get_age(self):
return self.age
person = Person("Alice", 25)
print(person.get_name()) # 输出 "Alice"
print(person.get_age()) # 输出 25
继承:
继承是指通过创建一个新类,从现有的类中继承属性和方法。通过继承,可以避免重复编写代码,提高代码的重用性。同时,通过继承还可以实现多层次的抽象和模块化的设计。
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
dog = Dog("Buddy")
cat = Cat("Whiskers")
print(dog.speak()) # 输出 "Woof!"
print(cat.speak()) # 输出 "Meow!"
多态:
多态是指相同的接口可以有不同的实现方式。通过多态,可以将不同的对象当作同一类型来对待,从而增加代码的灵活性和可扩展性。
class Shape:
def area(self):
pass
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius * self.radius
# 将Rectangle和Circle对象当作Shape类型的对象来对待,并调用相同的方法area,从而实现了对不同对象的统一处理
shapes = [Rectangle(4, 5), Circle(3)]
for shape in shapes:
print(shape.area())
2.2 提升代码的复用性:通过类和对象的创建实现代码的重用
通过类和对象的创建,可以实现代码的重用和模块化设计。将相关的属性和方法封装在一个类中,可以提高代码的可读性和可维护性;通过对象的创建,可以实现对类中属性和方法的共享和调用;通过继承的应用,可以实现代码的重用和扩展,避免了重复编写相同功能的代码。
小案例:
假设我们有一个表示汽车的类Car
,其中包含了汽车的颜色、品牌和型号等属性,以及加速、刹车和转向等方法。我们可以创建多个Car
对象,每个对象都具有相同的属性和方法,但是它们的具体属性值可能不同。
class Car:
def __init__(self, color, brand, model):
self.color = color
self.brand = brand
self.model = model
# 加速
def accelerate(self):
print(f"The {self.color} {self.brand} {self.model} is accelerating.")
# 刹车
def brake(self):
print(f"The {self.color} {self.brand} {self.model} is braking.")
# 转向
def turn(self, direction):
print(f"The {self.color} {self.brand} {self.model} is turning {direction}.")
# 创建两个Car对象,具有相同的属性和方法,但具体的属性值不同
car1 = Car("red", "Toyota", "Camry")
car2 = Car("blue", "Honda", "Civic")
# 调用对象的方法
car1.accelerate() # 输出 "The red Toyota Camry is accelerating."
car2.brake() # 输出 "The blue Honda Civic is braking."
代码重用性的好处:如果我们需要创建更多的汽车对象,只需简单地实例化新的对象即可,而无需重新编写类或方法。此外,如果我们需要修改汽车的某个属性或方法,只需在类定义中进行修改,所有的对象都会受到影响。
三、类和对象的基本概念
3.1 类和对象的定义和关系:类是对象的模板,对象是类的实例
类和对象之间的关系:
- 类是对象的模板或蓝图,它定义了对象应该具有的属性和方法。
- 对象是根据类的定义创建的一个具体实体,它具有类定义中所描述的属性和方法。
类和对象之间的关系:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def say_hello(self):
print(f"Hello, my name is {self.name} and I am {self.age} years old.")
# 创建Person类的对象
person1 = Person("Alice", 25)
person2 = Person("Bob", 30)
# 访问对象的属性和方法
print(person1.name) # 输出 "Alice"
print(person2.age) # 输出 30
person1.say_hello() # 输出 "Hello, my name is Alice and I am 25 years old."
person2.say_hello() # 输出 "Hello, my name is Bob and I am 30 years old."
3.2 属性和方法:类和对象具有的特征和行为
属性 | 方法和类 | 对象之间的关系:
- 属性是类或对象的特征或状态,用于描述对象的状态、特征或数据。
- 方法是类或对象的行为或操作,用于执行某些具体的动作或操作。
属性和方法在类和对象中的应用:
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def bark(self):
print(f"{self.name} is barking!")
def eat(self, food):
print(f"{self.name} is eating {food}.")
# 创建Dog类的对象
dog1 = Dog("Buddy", 3)
dog2 = Dog("Max", 5)
# 访问对象的属性
print(dog1.name) # 输出 "Buddy"
print(dog2.age) # 输出 5
# 调用对象的方法
dog1.bark() # 输出 "Buddy is barking!"
dog2.eat("bone") # 输出 "Max is eating bone."
3.3 初始化方法:init()方法的作用和使用
init()方法用于在创建对象时对对象进行初始化。它的作用是在新建对象时分配内存空间,并对对象的属性进行初始化,通常被放在类的定义中,并且必须以self
作为第一个参数。它可以接受任意数量的额外参数,用于初始化对象中的属性。
简单的Person类的定义:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
当创建一个Person对象时,必须提供name和age参数,Python会自动调用__init__()方法来创建person1对象并进行初始化。
创建了一个名为person1的Person对象:
person1 = Person("Alice", 25)
init()方法的作用是确保对象在创建时处于正确的状态,并且可以避免在后续操作中出现错误。通过使用__init__()方法,可以方便地对对象的属性进行初始化,并且可以在创建对象时自动完成这些操作,从而提高了代码的可读性和可维护性。
四、类的继承与多态
4.1 继承的概念和分类:单继承和多继承
继承是面向对象编程中的重要概念,它允许我们构建具有层次结构的类,并且可以在子类中重用和扩展父类的功能。单继承和多继承提供了不同的继承方式,可以根据具体需求选择适合的继承方式。通过继承,一个类可以派生出子类,从而使子类可以继承父类的属性和方法。这种机制允许我们创建具有层次结构的类,其中子类可以继承和扩展父类的功能。
- 单继承:
单继承是指一个子类只能继承一个父类的特性。在类定义时将父类作为参数传递给子类,实现单继承。子类会继承父类的所有属性和方法,并且可以在子类中添加新的属性和方法。
# 父类
class Parent:
def __init__(self):
self.parent_attr = "Parent attribute"
def parent_method(self):
print("Parent method")
# 子类
# 通过调用super().init()方法,子类可以调用父类的初始化方法来初始化父类的属性
class Child(Parent):
def __init__(self):
super().__init__() # 调用父类的初始化方法
self.child_attr = "Child attribute"
def child_method(self):
print("Child method")
创建一个Child对象并调用父类和子类的方法:
obj = Child()
obj.parent_method() # 输出 "Parent method"
obj.child_method() # 输出 "Child method"
print(obj.parent_attr) # 输出 "Parent attribute"
print(obj.child_attr) # 输出 "Child attribute"
- 多继承:
多继承是指一个子类可以同时继承多个父类的特性。在类定义时将多个父类作为参数传递给子类,实现多继承。
# 父类
class Parent1:
def parent1_method(self):
print("Parent1 method")
# 父类
class Parent2:
def parent2_method(self):
print("Parent2 method")
# 子类
class Child(Parent1, Parent2):
def child_method(self):
print("Child method")
创建一个Child对象并调用父类和子类的方法:
obj = Child()
obj.parent1_method() # 输出 "Parent1 method"
obj.parent2_method() # 输出 "Parent2 method"
obj.child_method() # 输出 "Child method"
ps:在多继承中,如果多个父类中有相同的方法,子类会按照继承的顺序优先调用第一个父类的方法。这种潜在的冲突可以通过方法重写或使用super()
来解决。
4.2 多态的实现:不同对象对相同方法的不同表现
多态是指不同对象对相同方法的不同表现。具体来说,多态允许我们在不考虑对象类型的情况下,调用同一个方法,不同的对象会对该方法做出不同的响应。
多态的实现:
class Animal:
def __init__(self, name):
self.name = name
# 方法没有任何实现,因为不同的动物会发出不同的声音
def make_sound(self):
pass
# 子类Dog和Cat重写了make_sound()方法,返回它们各自的声音
class Dog(Animal):
def make_sound(self):
return "Woof"
class Cat(Animal):
def make_sound(self):
return "Meow"
通过这种方式,我们可以创建一个Animal
对象,并调用make_sound()
方法,不同的对象会对该方法做出不同的响应:
# 创建一个Animal对象
animal = Animal("Generic animal")
# 创建一个Dog对象
dog = Dog("Fido")
# 创建一个Cat对象
cat = Cat("Whiskers")
# 调用make_sound()方法
print(animal.make_sound()) # 输出 None
print(dog.make_sound()) # 输出 "Woof"
print(cat.make_sound()) # 输出 "Meow"
在不考虑对象类型的情况下,我们可以调用相同的方法,并让不同的对象对该方法做出不同的响应。这种机制使得代码更加灵活和可扩展,同时也避免了冗长的判断语句和复杂的类型转换。
五、封装和访问控制
5.1 封装的概念和作用:将数据和操作封装在类中
封装是指将数据和操作封装在类中,以达到数据的保护和隐藏实现细节的目的。具体来说,封装可以通过以下两种方式实现:
- 将属性和方法设为私有的,即在属性和方法名称前面加上双下划线
__
。这样做可以防止属性被外部直接访问,同时也可以避免子类意外地修改父类的属性。如果需要访问或修改属性,可以通过getter
和setter
方法实现。 - 使用
@property
装饰器来创建getter
和setter
方法,使得我们可以像访问属性一样访问方法,从而隐藏实现细节。
封装的概念和作用:
class Person:
def __init__(self, name, age):
self.__name = name # 将属性设为私有的
self.__age = age
# 定义getter方法
@property
def name(self):
return self.__name
# 定义setter方法
@name.setter
def name(self, value):
self.__name = value
# 定义getter方法
@property
def age(self):
return self.__age
# 定义setter方法,可以加入了一些判断逻辑来确保数据的合法性
@age.setter
def age(self, value):
if value < 0:
# 传入的值不符合要求,则抛出ValueError异常
raise ValueError("Age cannot be negative")
self.__age = value
def display_info(self):
print(f"Name: {self.__name}, Age: {self.__age}")
通过创建Person
对象,并调用它们的方法,我们可以看到封装的效果:
# 创建一个Person对象
p = Person("Alice", 25)
# 通过getter方法获取属性的值
print(p.name) # 输出 "Alice"
print(p.age) # 输出 25
# 通过setter方法修改属性的值
p.name = "Bob"
p.age = 30
p.display_info() # 输出 "Name: Bob, Age: 30"
通过将数据和操作封装在类中,我们可以隐藏实现细节,保护数据的安全性,同时也提高了代码的可维护性和可扩展性。
5.2 公有、私有和受保护的访问控制:属性和方法的可见性和访问限制
属性和方法的可见性和访问限制是通过命名规则来实现的。
Python使用单下划线
和双下划线
作为前缀来表示不同级别的访问控制:
- 公有访问控制:没有任何前缀。公有属性和方法可以被类的实例、子类和其他代码直接访问。
- 私有访问控制:双下划线
__
作为前缀。私有属性和方法只能在类内部访问,无法通过实例、子类或其他代码直接访问。但是,在Python中,私有属性和方法并不是真正无法访问的,而是进行了名称修饰
,将其名称变更为_类名__属性名
或_类名__方法名
的形式,以实现一定程度的隐藏。 - 受保护的访问控制:单下划线
_
作为前缀。受保护属性和方法可以被类的实例和子类访问,但约定上不建议直接访问。这种访问控制主要是一种约定,用于告诉其他开发者某个属性或方法被认为是类内部使用的,不建议在外部直接使用。
公有、私有和受保护的访问控制:
class MyClass:
def __init__(self):
self.public_var = "Public" # 公有属性
self._protected_var = "Protected" # 受保护属性
self.__private_var = "Private" # 私有属性
def public_method(self):
print("This is a public method") # 公有方法
def _protected_method(self):
print("This is a protected method") # 受保护方法
def __private_method(self):
print("This is a private method") # 私有方法
# 创建一个类的实例
obj = MyClass()
# 访问公有属性和调用公有方法
print(obj.public_var) # 输出 "Public"
obj.public_method() # 输出 "This is a public method"
# 访问受保护属性和调用受保护方法
print(obj._protected_var) # 输出 "Protected"
obj._protected_method() # 输出 "This is a protected method"
# 访问私有属性和调用私有方法(通过名称修饰)
print(obj._MyClass__private_var) # 输出 "Private"
obj._MyClass__private_method() # 输出 "This is a private method"
总结起来,公有访问控制允许外部直接访问属性和方法,私有访问控制将属性和方法隐藏在类内部,受保护访问控制则提供了一种约定,告诉其他开发者某个属性或方法是类内部使用的。这些访问控制级别可以帮助我们封装数据和隐藏实现细节,以提高代码的可维护性和安全性。
六、魔法方法和特殊属性
6.1 魔法方法的介绍和使用:repr()、str()等
**魔法方法(Magic Method)**是一些特殊的方法,它们以双下划线 __
开头和结尾,例如 __repr__()
、__str__()
。这些方法在某些特定场景下会被自动调用,可以用来实现类的一些特殊行为。
__repr__()
:
__repr__()
是一个魔法方法,用于返回一个对象的字符串表示形式(通常是一段可执行的代码),以便在交互式环境中显示或调试时使用。如果没有定义 __repr__()
方法,则调用 print()
函数打印该对象时,默认输出的是该对象的内存地址。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return f"Person('{self.name}', {self.age})"
p = Person("Alice", 25)
print(p) # 输出 "Person('Alice', 25)"
__str__()
:
__str__()
是一个魔法方法,用于返回一个对象的字符串表示形式(通常是人类可读的文本),以便在用户界面或日志文件中使用。如果没有定义 __str__()
方法,则调用 print()
函数打印该对象时,会尝试调用 __repr__()
方法来获取字符串表示。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return f"Person('{self.name}', {self.age})"
def __str__(self):
return f"{self.name}, {self.age}"
# 输出的是 "Alice, 25",而不是默认的内存地址或__repr__()方法返回的字符串
p = Person("Alice", 25)
print(p) # 输出 "Alice, 25"
6.2 特殊属性的应用:dict、__name__等
**特殊属性(Special Attribute)**是一些预定义的属性,以双下划线 __
开头和结尾,例如 __dict__
、__name__
等。这些属性提供了有关对象或类的一些额外信息,可以在编程中使用。
__dict__
:
__dict__
是一个特殊属性,它是一个字典,包含了对象或类的所有属性和方法的名称和值。通过访问 __dict__
属性,我们可以动态获取对象或类的属性和方法,并对其进行操作或修改,可以根据需要修改、添加或删除对象的属性。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def greet(self):
print(f"Hello, my name is {self.name}.")
p = Person("Alice", 25)
# 访问对象的 __dict__ 属性,获取到该对象的属性和对应的值的字典形式
print(p.__dict__)
# 输出: {'name': 'Alice', 'age': 25}
# 修改对象的属性
p.age = 30
print(p.__dict__)
# 输出: {'name': 'Alice', 'age': 30}
# 添加新的属性
p.location = "New York"
print(p.__dict__)
# 输出: {'name': 'Alice', 'age': 30, 'location': 'New York'}
__name__
:
__name__
是一个特殊属性,它用于获取类或函数的名称。对于类而言,__name__
返回类的名称;对于函数而言,__name__
返回函数的名称。
class Person:
pass
def say_hello():
print("Hello!")
print(Person.__name__) # 输出 "Person"
print(say_hello.__name__) # 输出 "say_hello"
七、类的设计原则和模式
7.1 SOLID原则:单一职责、开闭原则、里式替换、接口隔离原则
SOLID 原则是一组设计原则,旨在创建可维护、可扩展和可重用的软件。这些原则包括单一职责、开闭原则、里式替换、接口隔离和依赖倒置原则,简称为 SOLID。
单一职责原则:
单一职责原则指一个类或模块只应该有一个单一的功能,也就是说,一个类或模块只应该有一个单一的责任。如果一个类或模块负责多个不同的任务,那么它将会变得难以维护、扩展和测试。
# 不遵循单一职责原则的代码,既负责保存员工信息到数据库,又负责计算员工税收信息
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
def save(self):
# 保存员工信息到数据库
pass
def calculate_tax(self):
# 计算员工税收信息
pass
# 遵循单一职责原则的代码
# 只负责表示员工信息
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
# 负责保存员工信息到数据库
class EmployeeDB:
def save(self, employee):
# 保存员工信息到数据库
pass
# 负责计算员工税收信息
class TaxCalculator:
def calculate_tax(self, employee):
# 计算员工税收信息
pass
开闭原则:
开闭原则指一个模块或类应该对扩展开放,对修改关闭。也就是说,应该通过扩展已有的代码来满足新的需求,而不是修改已有的代码。如果一个模块或类需要频繁地修改,那么它将会变得难以维护和测试。
# 不遵循开闭原则的代码
# 矩形
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def set_width(self, width):
self.width = width
def set_height(self, height):
self.height = height
def area(self):
return self.width * self.height
# 正方形,继承 Rectangle 类
class Square(Rectangle):
def __init__(self, side):
super().__init__(side, side)
# 重写了 set_width() 和 set_height() 方法
# 导致在修改正方形的一边时,另一边也会跟着变化,从而破坏了矩形的定义
def set_width(self, width):
self.width = width
self.height = width
def set_height(self, height):
self.width = height
self.height = height
# 遵循开闭原则的代码
# 将 Rectangle 和 Square 类都实现为继承自 Shape 类的子类,再分别实现它们自己的 area() 方法
class Shape:
def area(self):
pass
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
class Square(Shape):
def __init__(self, side):
self.side = side
def area(self):
return self.side ** 2
里式替换原则:
里式替换原则指子类应该能够替换掉它们的父类,并且程序仍然能够正常工作。也就是说,子类应该保持与父类相同的行为,而不是改变其原本的含义。
# 不遵循里式替换原则的代码
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def set_width(self, width):
self.width = width
def set_height(self, height):
self.height = height
def area(self):
return self.width * self.height
class Square(Rectangle):
def __init__(self, side):
super().__init__(side, side)
def set_width(self, width):
self.width = width
self.height = width
def set_height(self, height):
self.width = height
self.height = height
def print_area(rectangle):
rectangle.set_width(2)
rectangle.set_height(3)
print(f"Area: {rectangle.area()}")
r = Rectangle(3, 4)
s = Square(3)
print_area(r) # 输出 "Area: 6"
# 传递一个 Square 对象作为参数时,由于 Square 类重写了 set_width() 和 set_height() 方法,导致输出的面积不是预期的值
print_area(s) # 输出 "Area: 9"
# 遵循里式替换原则
# 可以将 Rectangle 和 Square 类都实现为继承自 Shape 类的子类,并分别实现它们自己的 area() 方法
接口隔离原则:
接口隔离原则指一个接口应该只包含被调用者需要的方法,而不应该包含被调用者不需要的方法。也就是说,接口应该尽可能地小,以减少对实现类的依赖和影响。
# 不遵循接口隔离原则的代码
class Document:
def __init__(self, content):
self.content = content
def print(self):
# 打印文档内容
pass
def fax(self):
# 传真文档内容
pass
def scan(self):
# 扫描文档内容
pass
class MultiFunctionPrinter:
def __init__(self, document):
self.document = document
def print(self):
self.document.print()
def fax(self):
self.document.fax()
def scan(self):
self.document.scan()
class OldFashionedPrinter:
def __init__(self, document):
self.document = document
def print(self):
self.document.print()
# 遵循接口隔离原则的代码
# 将 Printable、Faxable 和 Scanable 接口分别抽象出来,然后让它们各自实现自己需要的方法
class Printable:
def print(self):
pass
class Faxable:
def fax(self):
pass
class Scanable:
def scan(self):
pass
class Document:
def __init__(self, content):
self.content = content
class MultiFunctionPrinter(Printable, Faxable, Scanable):
def __init__(self, document):
self.document = document
def print(self):
self.document.print()
def fax(self):
self.document.fax()
def scan(self):
self.document.scan()
class OldFashionedPrinter(Printable):
def __init__(self, document):
self.document = document
def print(self):
self.document.print()
7.2 常见设计模式:工厂模式、单例模式、观察者模式等
工厂模式:
工厂模式是一种创建型设计模式,它提供了一种封装对象创建过程的方式。它使用一个共同的接口来创建不同类型的对象,而不必暴露具体的实例化逻辑。
class Shape:
def draw(self):
pass
class Circle(Shape):
def draw(self):
print("Drawing a circle")
class Square(Shape):
def draw(self):
print("Drawing a square")
class ShapeFactory:
# 静态方法
@staticmethod
# 根据传入的形状类型来创建相应的对象
def create_shape(shape_type):
if shape_type == "circle":
return Circle()
elif shape_type == "square":
return Square()
else:
raise ValueError("Invalid shape type")
# 使用工厂模式创建对象,隐藏了具体的实例化逻辑
shape = ShapeFactory.create_shape("circle")
shape.draw() # 输出 "Drawing a circle"
单例模式:
单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供了全局访问点来获取该实例。
# 单例模式确保在整个应用程序中只有一个实例存在
class Singleton:
_instance = None # 静态变量
# 静态方法
@staticmethod
def get_instance():
if Singleton._instance is None:
Singleton._instance = Singleton()
return Singleton._instance
# 使用单例模式获取对象实例
singleton1 = Singleton.get_instance()
singleton2 = Singleton.get_instance()
print(singleton1 is singleton2) # 输出 True
观察者模式:
观察者模式是一种行为型设计模式,它定义了对象之间的一种一对多的依赖关系,使得当一个对象的状态发生改变时,所有依赖它的对象都会收到通知并自动更新。
# 主题类
class Subject:
def __init__(self):
# _observers 列表来保存所有注册的观察者对象
self._observers = []
# 添加
def attach(self, observer):
self._observers.append(observer)
# 移除
def detach(self, observer):
self._observers.remove(observer)
# 通知
def notify(self):
for observer in self._observers:
observer.update()
# 观察者类
class Observer:
# 用于接收并处理通知
def update(self):
pass
# 具体的观察者类
class ConcreteObserver(Observer):
def update(self):
print("Received notification")
# 使用观察者模式
subject = Subject()
observer = ConcreteObserver()
subject.attach(observer)
# 所有已注册的观察者都会接收到通知并执行相应的操作
subject.notify() # 输出 "Received notification"
八、实践项目:创建一个简单的图书管理系统
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
self.is_borrowed = False
# 借阅图书
def borrow(self):
if self.is_borrowed:
print("This book is already borrowed.")
else:
self.is_borrowed = True
print("Successfully borrowed the book:", self.title)
# 归还图书
def return_book(self):
if self.is_borrowed:
self.is_borrowed = False
print("Successfully returned the book:", self.title)
else:
print("This book is not currently borrowed.")
class Library:
def __init__(self):
self.books = []
# 添加图书
def add_book(self, book):
self.books.append(book)
print("Added book:", book.title)
# 删除图书
def remove_book(self, book):
if book in self.books:
self.books.remove(book)
print("Removed book:", book.title)
else:
print("Book not found in the library.")
# 显示图书
def display_books(self):
if len(self.books) == 0:
print("No books in the library.")
else:
print("Books in the library:")
for book in self.books:
print(book.title, "by", book.author)
# 创建图书和图书馆对象
book1 = Book("Python Crash Course", "Eric Matthes")
book2 = Book("Clean Code", "Robert C. Martin")
book3 = Book("The Pragmatic Programmer", "Andrew Hunt and David Thomas")
library = Library()
# 添加图书到图书馆
library.add_book(book1)
library.add_book(book2)
library.add_book(book3)
# 显示图书馆的所有图书
library.display_books()
# 借阅一本图书
book2.borrow()
# 再次显示图书馆的所有图书
library.display_books()
# 归还图书
book2.return_book()
# 再次显示图书馆的所有图书
library.display_books()
你要成长,绝处也能逢生