网站首页 > 技术文章 正文
设计模式的几大原则
设计模式的原则是软件设计中用于创建更灵活、可维护和可扩展的系统的基本指导原则。这些原则帮助开发者在设计过程中做出更好的决策,避免常见的设计错误。以下是一些重要的设计模式原则:
1. 单一职责原则 (Single Responsibility Principle, SRP)
- 定义:一个类应该只有一个引起它变化的原因,即一个类只负责一项职责。
- 目的:减少类的复杂性,使其更易于理解和维护。如果一个类承担多种职责,那么每种职责的变化都可能影响到这个类。
2. 开放封闭原则 (Open/Closed Principle, OCP)
- 定义:软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。
- 目的:当需求变化时,可以通过扩展原有功能而不是修改已有代码来实现新的需求,从而提高系统的稳定性和灵活性。
3. 里氏替换原则 (Liskov Substitution Principle, LSP)
- 定义:子类型必须能够替换掉它们的基类型,且不影响程序的正确性。
- 目的:确保继承关系的正确性,即子类对象能完全替代父类对象,使得父类的调用者能够透明地使用子类的实例。
4. 依赖倒置原则 (Dependency Inversion Principle, DIP)
- 定义:高层模块不应该依赖于低层模块,两者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
- 目的:通过依赖抽象(接口或抽象类),而不是具体实现,来减少类之间的耦合性,提高系统的灵活性和可维护性。
5. 接口隔离原则 (Interface Segregation Principle, ISP)
- 定义:客户端不应该被迫依赖于它不使用的方法,应该将庞大的接口拆分为更小、更具体的接口,使得客户端只需了解它所需的接口。
- 目的:通过接口分离降低系统的复杂性,避免由于接口臃肿导致的实现类的复杂性。
6. 合成复用原则 (Composite Reuse Principle, CRP)
- 定义:优先使用对象组合(即“has-a”关系)而不是继承(即"is-a"关系)来达到代码复用的目的。
- 目的:通过组合来提高代码的复用性和灵活性,减少继承带来的紧耦合问题。
7. 迪米特法则 (Law of Demeter, LoD)
- 定义:一个对象应该对其他对象有尽可能少的了解,即“最少知识原则”。
- 目的:减少对象之间的依赖关系,降低系统的耦合度,从而提高系统的模块化和维护性。
8. 优先使用组合而不是继承
- 定义:与继承相比,优先使用对象组合来复用代码。
- 目的:通过组合,类之间的依赖性较低,系统结构更灵活,容易扩展。
总结
这些设计原则在实践中相互作用,共同促进了高质量的代码设计。理解并运用这些原则,可以帮助开发者设计出更易维护、更具弹性的软件系统。
设计模式的分类
在软件设计中,创建型、结构型和行为型是设计模式的三种主要分类。这三类模式根据其用途和作用范围的不同,解决软件开发中的不同问题。
创建型模式
定义:创建型模式关注对象的创建过程,主要解决对象创建时的复杂性问题。
作用:
- 为创建对象提供更灵活、更高效、更符合扩展需求的方法。
- 通过抽象化的方式控制对象创建的细节和时机。
常见创建型模式:
- 工厂方法模式(Factory Method):定义一个用于创建对象的接口,让子类决定实例化哪一个类。
- 抽象工厂模式(Abstract Factory):提供一个创建相关或依赖对象的接口,而无需指定具体类。
- 单例模式(Singleton):确保一个类只有一个实例,并提供一个全局访问点。
- 建造者模式(Builder):将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
- 原型模式(Prototype):通过复制现有对象来创建新对象,而不是通过实例化类。
结构型模式
定义:结构型模式关注对象的组织方式,主要解决如何更好地组织类和对象,使其更灵活、更高效。
作用:
- 处理对象的组合、继承和接口适配问题。
- 简化系统的结构,增强系统的可维护性和扩展性。
常见结构型模式:
- 适配器模式(Adapter):将一个类的接口转换成客户期望的另一个接口。
- 桥接模式(Bridge):将抽象部分与实现部分分离,使它们可以独立变化。
- 组合模式(Composite):将对象组合成树形结构以表示“整体-部分”层次结构,使客户可以一致地处理单个对象和组合对象。
- 装饰器模式(Decorator):动态地为对象添加新的职责,而不改变其结构。
- 外观模式(Facade):为子系统提供一个统一的接口,简化子系统的使用。
- 享元模式(Flyweight):通过共享尽量多的相似对象,减少内存使用。
- 代理模式(Proxy):为另一个对象提供一个替身或占位符,以控制对该对象的访问。
行为型模式
定义:行为型模式关注的是对象之间的职责分配和通信方式,主要解决对象交互和职责分配的问题。
作用:
- 处理对象之间的动态交互关系。
- 增强对象之间的松耦合,简化复杂的控制流。
- 关注如何通过对象之间的协作完成任务。
常见行为型模式:
- 策略模式(Strategy):定义一组算法,将每个算法封装到独立的类中,并且可以相互替换。
- 观察者模式(Observer):定义对象间一对多的依赖关系,当一个对象状态变化时,所有依赖它的对象都会收到通知。
- 命令模式(Command):将请求封装为对象,以便支持参数化的命令、队列、日志等功能。
- 责任链模式(Chain of Responsibility):将请求沿着处理者链传递,直到被某个处理者处理。
- 状态模式(State):允许对象在内部状态改变时改变其行为。
- 模板方法模式(Template Method):定义算法的框架,将一些步骤推迟到子类中实现。
- 中介者模式(Mediator):通过中介者对象封装对象之间的交互,使其解耦。
- 迭代器模式(Iterator):提供一种访问集合对象元素的方法,而不暴露其内部表示。
- 备忘录模式(Memento):保存对象的某个状态,以便在以后可以恢复。
- 访问者模式(Visitor):定义一个新操作,可以作用于一组对象,而不改变这些对象的类。
三者的对比如下:
分类 | 关注点 | 目标 | 模式示例 |
行为型 | 对象之间的职责分配与动态交互 | 简化对象间通信、增强灵活性与松耦合 | 策略、观察者、命令、状态、责任链、模板方法等 |
结构型 | 对象与类的组合方式 | 简化系统结构、优化对象之间的依赖关系 | 适配器、桥接、组合、装饰器、外观、代理等 |
创建型 | 对象的创建方式 | 提高对象创建的灵活性、控制复杂对象的创建过程 | 工厂方法、抽象工厂、单例、建造者、原型等 |
创建型模式
单例模式
单例模式(Singleton Pattern)是一种创建型设计模式,确保一个类只有一个实例,并提供全局访问点。
使用 C++11 引入的 std::once_flag 和 std::call_once,可以实现线程安全的单例模式。std::once_flag 是 C++ 标准库中的同步工具,用于确保某段代码只执行一次,通常用于初始化。
#include <iostream>
#include <mutex>
#include <memory> // std::unique_ptr
class Singleton {
private:
static std::unique_ptr<Singleton> instance; // 静态实例指针
static std::once_flag initFlag; // 用于线程安全初始化的标志
// 私有构造函数,防止直接实例化
Singleton() {
std::cout << "Singleton constructor called!" << std::endl;
}
public:
// 禁用拷贝构造和赋值运算符
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
// 获取单例实例的方法
static Singleton* getInstance() {
// 保证初始化代码只执行一次
std::call_once(initFlag, []() {
instance.reset(new Singleton());
});
return instance.get();
}
// 业务方法
void doSomething() {
std::cout << "Singleton instance doing something!" << std::endl;
}
};
// 初始化静态成员
std::unique_ptr<Singleton> Singleton::instance = nullptr;
std::once_flag Singleton::initFlag;
int main() {
// 获取单例实例
Singleton* s1 = Singleton::getInstance();
Singleton* s2 = Singleton::getInstance();
// 调用方法
s1->doSomething();
// 验证实例是否相同
if (s1 == s2) {
std::cout << "s1 and s2 are the same instance!" << std::endl;
}
return 0;
}
工厂方法
工厂方法模式(Factory Method Pattern)定义了一个创建对象的接口,但由子类决定实例化哪一个类。工厂方法将对象的实例化推迟到子类。
我们以 UI 控件(按钮 Button) 为例:
- 系统需要支持多种平台(如 Windows 和 Mac)。
- 不同平台的按钮具有不同的外观和行为。
- 客户端代码应该与按钮的具体实现无关,只需调用按钮的通用接口。
工厂方法模式的核心思路
- 将创建按钮的逻辑抽象为一个工厂方法,让子类工厂决定创建哪种具体按钮。
- 客户端只需通过工厂接口创建按钮,无需了解具体按钮的实现细节。
工厂方法模式代码实现
// 1. 定义按钮接口(抽象产品类)
#include <iostream>
#include <memory>
// 按钮接口
class Button {
public:
virtual ~Button() = default;
virtual void render() = 0; // 渲染按钮
virtual void onClick() = 0; // 按钮点击事件
};
// 2. 创建具体按钮类(具体产品类)
// Windows 风格按钮
class WindowsButton : public Button {
public:
void render() override {
std::cout << "Rendering Windows Button" << std::endl;
}
void onClick() override {
std::cout << "Windows Button Clicked!" << std::endl;
}
};
// Mac 风格按钮
class MacButton : public Button {
public:
void render() override {
std::cout << "Rendering Mac Button" << std::endl;
}
void onClick() override {
std::cout << "Mac Button Clicked!" << std::endl;
}
};
// 3. 创建按钮工厂接口(抽象工厂类)
// 按钮工厂接口
class ButtonFactory {
public:
virtual ~ButtonFactory() = default;
virtual std::unique_ptr<Button> createButton() = 0; // 工厂方法:创建按钮
};
// 4. 创建具体工厂类(具体工厂类)
// Windows 按钮工厂
class WindowsButtonFactory : public ButtonFactory {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<WindowsButton>();
}
};
// Mac 按钮工厂
class MacButtonFactory : public ButtonFactory {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<MacButton>();
}
};
int main() {
// 创建 Windows 风格按钮的工厂
std::unique_ptr<ButtonFactory> factory = std::make_unique<WindowsButtonFactory>();
auto button = factory->createButton(); // 使用工厂方法创建按钮
button->render();
button->onClick();
// 创建 Mac 风格按钮的工厂
factory = std::make_unique<MacButtonFactory>();
button = factory->createButton(); // 使用工厂方法创建按钮
button->render();
button->onClick();
return 0;
}
工厂方法模式的特点和分析
- 封装对象创建:客户端代码只与工厂接口和产品接口交互,不关心具体产品类的实现。
- 扩展性好:新增产品(如 Linux 按钮)时,只需添加对应的具体产品类和具体工厂类,无需修改现有代码。
- 符合开闭原则:扩展具体产品和具体工厂时,不需要修改抽象工厂和抽象产品的定义。
抽象工厂方法
抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,提供一个接口,用于创建一组相关或相互依赖的对象,而无需指定具体类。
核心思想
- 抽象工厂:定义创建对象的接口。
- 具体工厂:实现接口,负责生成具体产品。
- 产品接口:定义具体产品的通用方法。
- 具体产品:实现产品接口,表示具体的产品实例。
- 客户端:通过抽象工厂使用产品,而不依赖具体工厂或产品的实现。
抽象工厂模式的优势是将产品族的创建与使用分离,方便扩展。
使用场景
- 需要生成一组相关或相互依赖的对象(例如不同风格的按钮和文本框)。
- 需要避免依赖具体类,隐藏对象创建的实现细节。
- 需要支持产品族的扩展。
以下是使用抽象工厂模式的 C++ 实现示例。假设我们要创建两种风格的用户界面组件:Windows 风格和 Mac 风格。
#include <iostream>
#include <memory>
// 产品接口:按钮
class Button {
public:
virtual ~Button() = default;
virtual void render() = 0; // 渲染按钮
};
// 产品接口:文本框
class TextBox {
public:
virtual ~TextBox() = default;
virtual void render() = 0; // 渲染文本框
};
// 具体产品:Windows 按钮
class WindowsButton : public Button {
public:
void render() override {
std::cout << "Rendering Windows Button" << std::endl;
}
};
// 具体产品:Windows 文本框
class WindowsTextBox : public TextBox {
public:
void render() override {
std::cout << "Rendering Windows TextBox" << std::endl;
}
};
// 具体产品:Mac 按钮
class MacButton : public Button {
public:
void render() override {
std::cout << "Rendering Mac Button" << std::endl;
}
};
// 具体产品:Mac 文本框
class MacTextBox : public TextBox {
public:
void render() override {
std::cout << "Rendering Mac TextBox" << std::endl;
}
};
// 抽象工厂:UI 工厂
class UIFactory {
public:
virtual ~UIFactory() = default;
virtual std::unique_ptr<Button> createButton() = 0; // 创建按钮
virtual std::unique_ptr<TextBox> createTextBox() = 0; // 创建文本框
};
// 具体工厂:Windows UI 工厂
class WindowsUIFactory : public UIFactory {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<WindowsButton>();
}
std::unique_ptr<TextBox> createTextBox() override {
return std::make_unique<WindowsTextBox>();
}
};
// 具体工厂:Mac UI 工厂
class MacUIFactory : public UIFactory {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<MacButton>();
}
std::unique_ptr<TextBox> createTextBox() override {
return std::make_unique<MacTextBox>();
}
};
// 客户端
void renderUI(UIFactory& factory) {
auto button = factory.createButton(); // 创建按钮
auto textBox = factory.createTextBox(); // 创建文本框
button->render();
textBox->render();
}
int main() {
WindowsUIFactory windowsFactory;
MacUIFactory macFactory;
std::cout << "Rendering Windows UI:" << std::endl;
renderUI(windowsFactory); // 使用 Windows 工厂
std::cout << "\nRendering Mac UI:" << std::endl;
renderUI(macFactory); // 使用 Mac 工厂
return 0;
}
代码解析
- 产品接口: Button 和 TextBox 是两个独立的产品接口,分别定义了按钮和文本框的通用行为。
- 具体产品: WindowsButton 和 MacButton 分别是 Windows 和 Mac 风格的按钮实现。 WindowsTextBox 和 MacTextBox 分别是 Windows 和 Mac 风格的文本框实现。
- 抽象工厂: UIFactory 定义了创建按钮和文本框的方法。
- 具体工厂: WindowsUIFactory 和 MacUIFactory 实现了具体的产品创建逻辑。
- 客户端: renderUI 函数通过抽象工厂接口创建按钮和文本框,而不依赖于具体工厂或具体产品。
原型模式
原型模式允许通过复制现有对象来创建新对象,而不是直接实例化类。这种模式提供了一种简化对象创建的方式,尤其是在对象的创建过程非常复杂时。
组成部分:
- 原型接口(Prototype):定义一个 clone 方法,用于克隆自身。
- 具体原型(Concrete Prototype):实现 clone 方法,定义如何复制自身。
- 客户端(Client):使用原型接口创建新对象。
适用场景:
- 创建对象的成本较高,直接创建对象不够高效(例如需要大量计算或访问数据库)。
- 系统需要大量相似对象,且对象的状态可通过复制得到。
- 想隐藏具体类的实现细节,通过抽象接口操作对象。
C++实现示例:
假设我们有一个复杂的图形类需要频繁创建,例如一个带有位置和颜色的形状对象。
#include <iostream>
#include <string>
#include <memory>
// 原型接口
class Shape {
public:
virtual ~Shape() = default;
virtual Shape* clone() const = 0; // 克隆方法
virtual void draw() const = 0; // 展示形状信息
};
// 具体原型:圆形
class Circle : public Shape {
private:
int x_, y_, radius_;
std::string color_;
public:
Circle(int x, int y, int radius, const std::string& color)
: x_(x), y_(y), radius_(radius), color_(color) {}
// 实现克隆方法
Circle* clone() const override {
return new Circle(*this); // 使用拷贝构造函数实现深拷贝
}
void draw() const override {
std::cout << "Circle: Position(" << x_ << ", " << y_
<< "), Radius: " << radius_
<< ", Color: " << color_ << std::endl;
}
};
// 具体原型:矩形
class Rectangle : public Shape {
private:
int x_, y_, width_, height_;
std::string color_;
public:
Rectangle(int x, int y, int width, int height, const std::string& color)
: x_(x), y_(y), width_(width), height_(height), color_(color) {}
// 实现克隆方法
Rectangle* clone() const override {
return new Rectangle(*this); // 使用拷贝构造函数实现深拷贝
}
void draw() const override {
std::cout << "Rectangle: Position(" << x_ << ", " << y_
<< "), Width: " << width_
<< ", Height: " << height_
<< ", Color: " << color_ << std::endl;
}
};
// 客户端
int main() {
// 创建一个圆形原型
Shape* circlePrototype = new Circle(10, 20, 15, "Red");
Shape* rectanglePrototype = new Rectangle(5, 5, 30, 40, "Blue");
// 克隆圆形对象
Shape* clonedCircle = circlePrototype->clone();
Shape* clonedRectangle = rectanglePrototype->clone();
// 显示克隆对象信息
clonedCircle->draw();
clonedRectangle->draw();
// 清理资源
delete circlePrototype;
delete rectanglePrototype;
delete clonedCircle;
delete clonedRectangle;
return 0;
}
建造者模式
什么是建造者模式?
建造者模式(Builder Pattern)是一种创建型设计模式,它允许我们一步一步地构建复杂对象。与直接用构造函数创建对象不同,建造者模式将对象的创建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
解决的问题
在对象创建过程中,某些对象的构造过程可能会变得复杂且繁琐,比如需要设置多个属性或依赖于其他对象。构造函数的参数列表可能会变得过长,不仅难以维护,还容易出现错误。
建造者模式解决了以下问题:
- 简化对象创建过程:将复杂对象的创建步骤分解并封装在不同的方法中,使对象的创建过程更加清晰。
- 解耦对象的创建和表示:将对象的创建过程与它们的最终表示形式分离开来,使得同样的构建过程可以创建不同的对象。
- 提高代码可读性和维护性:通过将对象的构建步骤分离出来,使代码更具可读性和维护性。
建造者模式的组成
建造者模式通常包含以下几部分:
- 产品(Product):最终构建出来的复杂对象。通常是一个包含多个部分的对象。
- 抽象建造者(Builder):定义创建产品各个部分的抽象方法。
- 具体建造者(ConcreteBuilder):实现抽象建造者接口,负责具体构建产品的各个部分,并提供一个用于获取最终产品的接口。
- 指挥者(Director):负责管理构建过程。它使用建造者来构建产品,通常会按照特定的顺序构建产品的各个部分。
- 客户端(Client):使用建造者来构建产品,但不需要关心产品的具体构建细节。
C++ 代码示例:
假设我们需要构建一个复杂的对象,表示一个“汽车”对象,包含发动机、车轮和车身等部件。我们使用建造者模式来解耦汽车的构建过程。
#include <iostream>
#include <string>
// 产品类:汽车
class Car {
public:
void setEngine(const std::string& engine) {
engine_ = engine;
}
void setWheels(int wheels) {
wheels_ = wheels;
}
void setBody(const std::string& body) {
body_ = body;
}
void show() {
std::cout << "Car details: " << std::endl;
std::cout << "Engine: " << engine_ << std::endl;
std::cout << "Wheels: " << wheels_ << std::endl;
std::cout << "Body: " << body_ << std::endl;
}
private:
std::string engine_;
int wheels_;
std::string body_;
};
// 抽象建造者:建造者类
class CarBuilder {
public:
virtual void buildEngine() = 0;
virtual void buildWheels() = 0;
virtual void buildBody() = 0;
virtual Car* getResult() = 0;
virtual ~CarBuilder() = default;
};
// 具体建造者:构建具体汽车
class SportsCarBuilder : public CarBuilder {
private:
Car* car;
public:
SportsCarBuilder() {
car = new Car();
}
void buildEngine() override {
car->setEngine("V8 Engine");
}
void buildWheels() override {
car->setWheels(4); // 运动型车需要四个轮子
}
void buildBody() override {
car->setBody("Sports Body");
}
Car* getResult() override {
return car;
}
~SportsCarBuilder() {
delete car;
}
};
// 具体建造者:构建SUV
class SUVCarBuilder : public CarBuilder {
private:
Car* car;
public:
SUVCarBuilder() {
car = new Car();
}
void buildEngine() override {
car->setEngine("V6 Engine");
}
void buildWheels() override {
car->setWheels(4); // SUV车也需要四个轮子
}
void buildBody() override {
car->setBody("SUV Body");
}
Car* getResult() override {
return car;
}
~SUVCarBuilder() {
delete car;
}
};
// 指挥者:指导建造过程
class CarDirector {
private:
CarBuilder* builder;
public:
CarDirector(CarBuilder* builder) : builder(builder) {}
// 指挥者负责建造整个汽车
void construct() {
builder->buildEngine();
builder->buildWheels();
builder->buildBody();
}
Car* getCar() {
return builder->getResult();
}
};
// 客户端
int main() {
// 选择建造者
CarBuilder* builder = new SportsCarBuilder();
CarDirector director(builder);
director.construct();
Car* car = director.getCar();
car->show();
// 清理资源
delete car;
delete builder;
// 构建另一种类型的车
builder = new SUVCarBuilder();
director = CarDirector(builder);
director.construct();
car = director.getCar();
car->show();
delete car;
delete builder;
return 0;
}
代码解析
- Car 是产品类,它包含汽车的多个部分(如发动机、车轮和车身)。
- CarBuilder 是抽象建造者,定义了构建汽车各个部分的接口。
- SportsCarBuilder 和 SUVCarBuilder 是具体建造者,负责实现如何构建不同类型的汽车。
- CarDirector 是指挥者,负责协调构建过程并决定构建的顺序。
- Client 使用建造者模式来创建具体的汽车。
总结
建造者模式提供了一种灵活的方式来创建复杂对象。它将对象的创建过程分解成多个步骤,并允许这些步骤以不同的顺序或方式组合,从而使得同样的构造过程可以生成不同的对象。通过建造者模式,代码的可读性和可维护性得到了提升,并且减少了创建复杂对象时可能出现的错误。
- 上一篇: GIA证书怎么看 gia证书怎么看有无奶咖
- 下一篇: 趣谈 23 种设计模式(多图 + 代码)
猜你喜欢
- 2024-12-19 C++的23种设计模式(上篇-创建型模式)
- 2024-12-19 25000 字详解 23 种设计模式(多图 + 代码)
- 2024-12-19 开源的的二维绘图引擎,EChart在用的图形渲染器——ZRender
- 2024-12-19 手机拍大片诀窍记心间 掌上PS应用合集
- 2024-12-19 搞科研常用技能和绘图学习 科研绘图工具
- 2024-12-19 「服装小知识」服装各部位名称学习(中英对照)
- 2024-12-19 10分钟教你如何看懂GIA证书 怎么看gia证书的详细信息
- 2024-12-19 图片四个角怎么能做成圆弧角?这几种制作方法操作起来很简单!
- 2024-12-19 苹果梨篇:大庙香水梨 大香水梨品种介绍
- 2024-12-19 教你用OpenCV 和 Python实现圆物检测
- 02-21走进git时代, 你该怎么玩?_gits
- 02-21GitHub是什么?它可不仅仅是云中的Git版本控制器
- 02-21Git常用操作总结_git基本用法
- 02-21为什么互联网巨头使用Git而放弃SVN?(含核心命令与原理)
- 02-21Git 高级用法,喜欢就拿去用_git基本用法
- 02-21Git常用命令和Git团队使用规范指南
- 02-21总结几个常用的Git命令的使用方法
- 02-21Git工作原理和常用指令_git原理详解
- 最近发表
- 标签列表
-
- cmd/c (57)
- c++中::是什么意思 (57)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- java是值传递还是引用传递 (58)
- 无效的列索引 (74)