现在将软件开发中的核心方法论分为几类,并结合 Python、Java 和 C++ 的实例进行说明。这些方法论是解决复杂问题的通用思维模式。
一、 核心方法论概述
-
抽象与具体
- 抽象:隐藏复杂的实现细节,只暴露必要的接口或属性。这是管理复杂性的基石。
- 具体:抽象概念的具体实现。关注“如何做”的细节。
- 关系:通过抽象定义契约,通过具体实现功能。
-
一般与特殊(继承与多态)
- 一般:定义通用的类、接口或模板。代表共性。
- 特殊:从一般化定义中派生出特定化的版本,可以继承、覆盖或特化。代表特性。
- 关系:实现代码复用和行为扩展。
-
分而治之
- 将一个大问题分解为若干个相同或相似的小问题,递归求解,再合并结果。常见于算法设计(如排序、搜索)。
-
组合优于继承
- 通过将对象作为其他对象的字段(组合)或通过接口协作来构建复杂功能,而非单纯依靠类继承树。这提高了灵活性和可维护性。
-
关注点分离
- 将系统分解为不同的部分,每一部分只负责一个特定的功能领域(如数据访问、业务逻辑、用户界面)。MVC、分层架构就是其体现。
-
契约式设计
- 明确规定函数或方法的前置条件、后置条件和不变式。确保组件在满足约定时正确工作。
二、 实例说明
我们将用三个例子来贯穿上述方法,并用三种语言实现。
例子场景:图形计算系统
- 定义一个抽象的
Shape。 - 实现具体的
Circle和Rectangle。 - 使用组合设计一个复杂的
Drawing(画布)。 - 演示分而治之(计算面积之和)。
- 演示契约设计(输入验证)。
1. 抽象与具体 & 一般与特殊
Python
from abc import ABC, abstractmethod
import math
# 抽象 & 一般
class Shape(ABC):
@abstractmethod
def area(self):
pass # 抽象方法,无具体实现
def describe(self): # 具体方法,可被继承
return f"I am a shape."
# 具体 & 特殊
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self): # 实现抽象方法
return math.pi * self.radius ** 2
def describe(self): # 覆盖父类方法
return f"I am a Circle with radius {self.radius}."
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
# 使用
shapes = [Circle(5), Rectangle(3, 4)]
for s in shapes:
print(s.describe(), f"Area: {s.area()}")
# 输出体现了多态:同一个接口,不同行为
Java
// 抽象 & 一般
abstract class Shape {
public abstract double area(); // 抽象方法
public String describe() { // 具体方法
return "I am a shape.";
}
}
// 具体 & 特殊
class Circle extends Shape {
private double radius;
public Circle(double radius) { this.radius = radius; }
@Override
public double area() {
return Math.PI * radius * radius;
}
@Override
public String describe() {
return "I am a Circle with radius " + radius;
}
}
class Rectangle extends Shape {
private double width, height;
public Rectangle(double w, double h) { width = w; height = h; }
@Override
public double area() { return width * height; }
}
public class Main {
public static void main(String[] args) {
Shape[] shapes = {new Circle(5), new Rectangle(3, 4)};
for (Shape s : shapes) {
System.out.println(s.describe() + " Area: " + s.area());
}
}
}
C++
#include <iostream>
#include <vector>
#include <string>
#include <cmath>
using namespace std;
// 抽象 & 一般
class Shape {
public:
virtual double area() const = 0; // 纯虚函数,使类成为抽象类
virtual string describe() const { return "I am a shape."; }
virtual ~Shape() {} // 虚析构函数,确保正确释放资源
};
// 具体 & 特殊
class Circle : public Shape {
double radius;
public:
Circle(double r) : radius(r) {}
double area() const override {
return M_PI * radius * radius;
}
string describe() const override {
return "I am a Circle with radius " + to_string(radius);
}
};
class Rectangle : public Shape {
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double area() const override { return width * height; }
};
int main() {
vector<Shape*> shapes = {new Circle(5), new Rectangle(3, 4)};
for (auto s : shapes) {
cout << s->describe() << " Area: " << s->area() << endl;
delete s; // 清理内存
}
return 0;
}
2. 组合优于继承 & 关注点分离
我们创建一个 Drawing 类,它组合了多个 Shape 对象。Drawing 只负责管理形状集合,不关心单个形状的具体计算(关注点分离)。
Python
class Drawing:
def __init__(self):
self.shapes = [] # 组合:持有其他对象的引用
def add_shape(self, shape: Shape): # 依赖于抽象Shape,而非具体类
self.shapes.append(shape)
def total_area(self):
# 分而治之:将“求总面积”分解为“求每个形状面积并求和”
total = 0
for shape in self.shapes:
total += shape.area() # 委托给各个具体对象
return total
def describe_all(self):
return [s.describe() for s in self.shapes]
# 使用
drawing = Drawing()
drawing.add_shape(Circle(5))
drawing.add_shape(Rectangle(3, 4))
print(f"Total Area: {drawing.total_area()}")
print(f"Descriptions: {drawing.describe_all()}")
Java
import java.util.ArrayList;
import java.util.List;
class Drawing {
private List<Shape> shapes = new ArrayList<>(); // 组合
public void addShape(Shape shape) {
shapes.add(shape);
}
public double totalArea() {
double total = 0;
for (Shape s : shapes) {
total += s.area(); // 委托
}
return total;
}
public List<String> describeAll() {
List<String> desc = new ArrayList<>();
for (Shape s : shapes) {
desc.add(s.describe());
}
return desc;
}
}
public class Main {
public static void main(String[] args) {
Drawing drawing = new Drawing();
drawing.addShape(new Circle(5));
drawing.addShape(new Rectangle(3, 4));
System.out.println("Total Area: " + drawing.totalArea());
System.out.println("Descriptions: " + drawing.describeAll());
}
}
C++
class Drawing {
vector<Shape*> shapes; // 组合:使用指针集合
public:
void addShape(Shape* s) {
shapes.push_back(s);
}
double totalArea() const {
double total = 0;
for (const auto& s : shapes) {
total += s->area(); // 委托
}
return total;
}
vector<string> describeAll() const {
vector<string> desc;
for (const auto& s : shapes) {
desc.push_back(s->describe());
}
return desc;
}
~Drawing() {
for (auto s : shapes) {
delete s; // 清理资源
}
}
};
int main() {
Drawing drawing;
drawing.addShape(new Circle(5));
drawing.addShape(new Rectangle(3, 4));
cout << "Total Area: " << drawing.totalArea() << endl;
auto desc = drawing.describeAll();
for (const auto& d : desc) {
cout << d << endl;
}
// drawing析构时会自动delete所有shape
return 0;
}
3. 契约式设计
我们通过前置条件验证来演示契约思想。
Python (使用类型提示和 assert 或 if 检查)
class ValidatedRectangle(Rectangle):
def __init__(self, width, height):
# 前置条件契约:宽度和高度必须为正数
if not (isinstance(width, (int, float)) and width > 0):
raise ValueError("Width must be a positive number.")
if not (isinstance(height, (int, float)) and height > 0):
raise ValueError("Height must be a positive number.")
# 不变式契约:创建后,width和height应保持不变(除非提供修改方法并同样验证)
super().__init__(width, height)
# 后置条件契约示例:area() 的返回值应 >= 0
# 在这个简单例子中,由于构造时已验证,后置条件自然满足。
Java (使用 IllegalArgumentException)
class ValidatedRectangle extends Rectangle {
public ValidatedRectangle(double width, double height) {
super(width, height); // 先调用父类构造
// 前置条件验证(更严格的做法应在赋值前验证)
if (width <= 0 || height <= 0) {
throw new IllegalArgumentException("Width and height must be positive.");
}
// 不变式:this.width > 0 && this.height > 0
}
}
C++ (使用 exceptions 或 assert)
class ValidatedRectangle : public Rectangle {
public:
ValidatedRectangle(double width, double height) : Rectangle(width, height) {
// 前置条件验证
if (width <= 0 || height <= 0) {
throw std::invalid_argument("Width and height must be positive.");
}
// 或者使用 assert (仅在调试模式生效)
// assert(width > 0 && height > 0);
}
// 后置条件:area() > 0 在构造函数验证后成立
};
4. 其他重要方法补充
-
迭代与递归:解决问题的一种重复机制。
- 实例:计算斐波那契数列。
# Python 迭代 def fib_iterative(n): a, b = 0, 1 for _ in range(n): a, b = b, a + b return a # Python 递归 (分而治之的体现) def fib_recursive(n): if n <= 1: return n # 基本情况 return fib_recursive(n-1) + fib_recursive(n-2) # 分解问题 -
配置与约定:通过外部配置(如文件、环境变量)改变行为,或遵循特定约定(如Java Bean的getter/setter)。
-
依赖注入:将对象的依赖从内部创建改为外部传入,实现控制反转,提高可测试性。Spring框架是典范。
总结
| 方法 | 核心思想 | 示例体现 |
|---|---|---|
| 抽象与具体 | 隐藏细节,暴露接口 | Shape 抽象类与 Circle 具体类 |
| 一般与特殊 | 继承共性,扩展特性 | Shape 作为父类,Rectangle 继承并实现 area() |
| 分而治之 | 分解、解决、合并 | Drawing.total_area() 循环求和 |
| 组合优于继承 | 通过包含而非继承构建功能 | Drawing 类包含 Shape 列表 |
| 关注点分离 | 模块各司其职 | Shape 管计算,Drawing 管集合,Main 管流程 |
| 契约式设计 | 明确前提、保证结果 | ValidatedRectangle 的构造参数验证 |
这些方法不是孤立的,而是交织在优秀的软件设计中。例如,Drawing 类使用组合(持有Shape列表),遵循关注点分离(只管理集合),其 total_area 方法体现了分而治之,并且它依赖于抽象的 Shape 类,通过多态(一般与特殊)调用具体实现,在添加形状时可能还会用到契约验证。掌握这些思维模式,能让你在使用任何语言进行开发时,都能写出结构清晰、易于维护的代码。
412

被折叠的 条评论
为什么被折叠?



