设计模式学习(二)——《大话设计模式》

本文详细介绍了设计模式学习的第二部分,包括属性和修饰符的使用、封装的概念及其优势、继承的特性与目的、以及多态的工作原理和优点。通过实例展示了如何在Java和C++/Python等语言中应用这些概念。
摘要由CSDN通过智能技术生成

设计模式学习(二)——《大话设计模式》

1.属性和修饰符

属性(Attributes)

属性是类中定义的变量,用于存储对象的状态信息。它们代表了对象的特征,比如一个Person类可能有name和age属性。在设计模式中,如何定义和使用属性对于实现特定模式的效果至关重要。

私有属性:一般用于类内部,不希望外部直接访问。在Java中使用private修饰符,在Python中可以通过前缀__来表示(虽然这只是一种约定,并非真正意义上的私有)。
公有属性:可以被外部访问和修改。通常公开访问的属性应该谨慎使用,以避免破坏对象的封装性。
受保护属性:这类属性通常只允许在类本身以及其子类中访问,使用protected修饰符。

代码示例:

class Person:
    def __init__(self, name, age):
        self.name = name  # 公有属性
        self.__age = age  # 私有属性,通过双下划线前缀表示

    def display_info(self):
        print(f"Name: {self.name}, Age: {self.__age}")

# 使用类
person = Person("Alice", 30)
print(person.name)  # 正常访问
# print(person.__age)  # 会引发错误,因为__age是私有属性
person.display_info()  # 通过类的公有方法访问私有属性

修饰符(Modifiers)

修饰符定义了对类、方法或属性的访问控制级别。它们在不同的编程语言中可能有所不同,但常见的有:

public(公有):成员对所有类可见。
private(私有):成员只对定义它们的类可见。
protected(受保护):成员对定义它们的类及其子类可见。
default(默认,仅Java):如果没有指定修饰符,则使用默认访问级别,成员对同一包内的类可见。
final(最终,主要用于Java):表示变量值一旦被赋值后不能改变,方法不能被重写,类不能被继承。
在设计模式中的应用
单例模式(Singleton):通常将构造函数设置为私有(private),以确保只能从类内部创建实例。
工厂模式(Factory Method):可以使用受保护(protected)或私有(private)修饰符来限制对工厂方法的访问,从而控制对象的创建。
装饰器模式(Decorator):通过扩展类的功能而不修改其原始代码,通常需要对原始类的属性和方法进行访问,这可能涉及到对属性和方法访问级别的调整。
策略模式(Strategy):可以将策略接口作为公有(public)属性或方法暴露出来,以便在运行时切换策略。

代码示例

#@property装饰器允许你将一个方法转化为只读属性,并可以结合setter装饰器来控制属性的修改。
class Person:
    def __init__(self, name, age):
        self._name = name  # 使用单下划线定义受保护属性
        self.__age = age  # 私有属性

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self, value):
        if value < 0:
            raise ValueError("Age cannot be negative")
        self.__age = value

    @property
    def name(self):
        return self._name

# 使用类
person = Person("Bob", 25)
print(person.name)  # 访问公有属性
print(person.age)  # 通过@property装饰的方法访问私有属性
person.age = 26  # 修改私有属性值
# person.age = -1  # 尝试设置不合法的年龄,会引发ValueError

2.封装

封装是面向对象编程(OOP)的三大基本特征之一,另外两个是继承和多态。封装的核心思想是将对象的数据(属性)和操作这些数据的方法绑定在一起,形成一个紧密的单元,并对外隐藏对象的具体实现细节。这样做的目的是提高代码的安全性、可维护性和复用性。

封装的主要目的:
  • 隐藏实现细节:用户只需要知道对象提供了哪些方法来操作数据,而不需要知道这些方法是如何实现的。
  • 简化接口:通过将复杂的实现细节隐藏起来,只暴露简单的接口给外部使用,使得对象更加易于理解和使用。
  • 增强安全性:限制对某些内部数据的直接访问,只能通过对象提供的方法来修改这些数据,可以避免外部的非法访问和修改,保护对象的状态安全。
封装的优势:
  • 良好的封住能够减少耦合;
  • 类内部的实现可以自由的修改;
  • 类具有清晰的对外接口;
实现封装:

在面向对象语言中,封装通常通过使用访问修饰符(如private、protected、public)来实现。通过将类的某些属性或方法声明为私有(private),就可以防止外部直接访问这些成员,只能通过公有(public)方法来间接访问或修改这些私有成员。

代码示例:

class Account:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.__balance = balance  # 私有属性

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            print(f"Added {amount} to the balance")
        else:
            print("Deposit amount must be positive")

    def withdraw(self, amount):
        if 0 < amount <= self.__balance:
            self.__balance -= amount
            print(f"Withdrew {amount} from the balance")
        else:
            print("Invalid withdrawal amount")

    def get_balance(self):
        return self.__balance

# 使用
acc = Account("John", 100)
acc.deposit(50)
acc.withdraw(75)
print(acc.get_balance())  # 通过公有方法访问私有属性

# 直接访问__balance会失败
# print(acc.__balance)  # AttributeError

// 封装
class Person {
private:
    // 私有属性
    std::string name;

public:
    // 公共方法来访问私有属性
    void setName(std::string n) {
        name = n;
    }

    std::string getName() {
        return name;
    }
};

public class Person {
    private String name; // 私有属性

    // 公共方法来访问私有属性
    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
}

3.继承

继承是面向对象编程(OOP)的另一个核心概念,它允许我们定义一个类(子类或派生类)来继承另一个类(基类或父类)的属性和方法。继承机制使得子类可以重用父类的代码,这不仅可以减少重复代码,还可以实现代码的组织和模块化。

继承的特点:
  • 子类拥有父类非private的属性和功能;
  • 子类具有自己的属性和功能,即子类可以扩展父类没有的属性和功能;
  • 子类还可以以自己的方式实现父类的功能(方法重写);
  • 继承使的所有子类公共的部分都放在了父类,使的代码得到了共享,避免了重复,继承可使得修改或者扩展而来的实现都较为容易。
  • 继承的缺点
    • 父类改变,则子类也会随之改变
    • 继承破坏包装,父类实现细节会暴露给子类
    • 继承是一种类与类之间的强耦合的关系
继承的主要目的:
  • 代码重用:通过继承,子类可以使用父类中定义的方法和属性,无需重新编写相同的代码。
  • 实现多态:子类可以根据需要覆盖或扩展父类的行为,提供新的实现,这是多态性的基础。
  • 建立类之间的关系:继承可以表达不同类之间的层次关系,为设计更复杂的系统提供了一种自然的方式。
继承的类型:
  • 单继承:一个子类只继承自一个父类。
  • 多重继承:一个子类可以同时继承多个父类。在某些语言中(如Python),这是支持的,但在其他语言中(如Java),则通过接口实现类似功能。

代码示例

# 定义一个基类(父类)
class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        raise NotImplementedError("Subclass must implement abstract method")

# 定义一个子类,继承自Animal
class Dog(Animal):
    def speak(self):
        return f"{self.name} says Woof!"

# 定义另一个子类,也继承自Animal
class Cat(Animal):
    def speak(self):
        return f"{self.name} says Meow!"

# 使用子类
dog = Dog("Buddy")
print(dog.speak())  # 输出: Buddy says Woof!

cat = Cat("Whiskers")
print(cat.speak())  # 输出: Whiskers says Meow!

// 基类
class Animal {
public:
    void eat() {
        std::cout << "I can eat!" << std::endl;
    }
};

// 派生类
class Dog : public Animal {
public:
    void bark() {
        std::cout << "I can bark! Woof woof!" << std::endl;
    }
};

// 基类
class Animal {
    void eat() {
        System.out.println("I can eat!");
    }
}

// 派生类
class Dog extends Animal {
    void bark() {
        System.out.println("I can bark! Woof woof!");
    }
}

4.多态

多态表示不同的对象可以执行相同的动作,但是要通过它们自己的代码实现;多态性允许我们以统一的接口操作不同类型的对象,而具体的行为则取决于对象的实际类型。这种机制使得代码更加灵活和可扩展。

第一:子类以父类的身份出现;

第二:子类在工作时以自己的方式来实现;

第三:子类以父类的身份出现时,子类特有的方法和属性不可使用;

多态的工作原理:

当方法被调用时,无论对象是否被转换为其父类,都只有位于对象继承链最末端的方法实现会被调用。

  • 接口的统一:多态性要求不同的对象能响应同一消息(或方法调用),即它们有共同的接口。
  • 实现的差异:虽然接口统一,但不同对象对同一消息可以有不同的响应方式,即它们的实现可以不同。
多态的优点:
  • 提高代码的可复用性:可以通过抽象类或接口编写通用的代码,这些代码可以与任何具体实现协作。
  • 提高代码的扩展性:可以引入新的对象类型,而无需修改使用旧对象类型的代码。
  • 提高代码的可维护性:多态性可以帮助隐藏具体实现的细节,使得代码更易于理解和维护。

代码示例:

class Animal:
    def speak(self):
        pass

class Dog(Animal):
    def speak(self):
        return "Woof!"

class Cat(Animal):
    def speak(self):
        return "Meow!"

def animal_sound(animal):
    print(animal.speak())

# 创建Dog和Cat的实例
dog = Dog()
cat = Cat()

# 调用函数,传入不同的对象
animal_sound(dog)  # 输出: Woof!
animal_sound(cat)  # 输出: Meow!

'''函数animal_sound接受一个Animal类型的参数。由于Dog和Cat都是Animal的子类,它们都可以作为参数传递给这个函数。函数内部调用了animal.speak(),具体调用哪个版本的speak方法取决于传入对象的实际类型。这就是多态性的体现'''
// 基类
class Shape {
public:
    virtual void draw() = 0; // 纯虚函数,提供接口框架
};

// 派生类1
class Circle : public Shape {
public:
    void draw() override {
        std::cout << "Drawing Circle" << std::endl;
    }
};

// 派生类2
class Square : public Shape {
public:
    void draw() override {
        std::cout << "Drawing Square" << std::endl;
    }
};

void drawShape(Shape* shape) {
    shape->draw();
}

// 基类
abstract class Shape {
    abstract void draw(); // 抽象方法
}

// 派生类1
class Circle extends Shape {
    void draw() {
        System.out.println("Drawing Circle");
    }
}

// 派生类2
class Square extends Shape {
    void draw() {
        System.out.println("Drawing Square");
    }
}

public class TestPolymorphism {
    public static void drawShape(Shape shape) {
        shape.draw();
    }

    public static void main(String[] args) {
        Shape circle = new Circle();
        Shape square = new Square();

        drawShape(circle);
        drawShape(square);
    }
}

void draw() {
System.out.println(“Drawing Square”);
}
}

public class TestPolymorphism {
public static void drawShape(Shape shape) {
shape.draw();
}

public static void main(String[] args) {
    Shape circle = new Circle();
    Shape square = new Square();

    drawShape(circle);
    drawShape(square);
}

}


  • 18
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值