代码复用(Code Reuse)是软件工程中的核心原则,通过避免重复代码来提高开发效率和代码质量。本文总结计算机语言特性中的代码复用,包括函数、类、泛型等机制,并对比不同语言的实现方式。
函数级复用
函数是代码复用的基础单元,通过封装可重复使用的逻辑来避免代码重复。
函数调用
函数调用是最直接的复用方式,将重复的逻辑封装成函数,在需要时调用。
# Python
def calculate_tax(amount, rate):
return amount * rate
tax1 = calculate_tax(100, 0.1)
tax2 = calculate_tax(200, 0.15)
// TypeScript
function calculateTax(amount: number, rate: number): number {
return amount * rate;
}
const tax1 = calculateTax(100, 0.1);
const tax2 = calculateTax(200, 0.15);
// Java
public double calculateTax(double amount, double rate) {
return amount * rate;
}
double tax1 = calculateTax(100, 0.1);
double tax2 = calculateTax(200, 0.15);
// C++
double calculateTax(double amount, double rate) {
return amount * rate;
}
double tax1 = calculateTax(100, 0.1);
double tax2 = calculateTax(200, 0.15);
高阶函数
高阶函数(Higher-Order Function)接受函数作为参数或返回函数,实现更抽象的复用。
// TypeScript - 函数作为参数
const numbers = [1, 2, 3, 4];
const doubled = numbers.map((x) => x * 2); // map 接受函数作为参数
# Python - 返回函数
def multiplier(factor):
def multiply(x):
return x * factor
return multiply
times2 = multiplier(2) # 返回新函数
print(times2(5)) # 10
// Java - 函数作为参数
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
List<Integer> doubled = numbers.stream()
.map(x -> x * 2) // map 接受 lambda 表达式
.collect(Collectors.toList());
// Java - 使用函数式接口
Function<Integer, Integer> multiplier(int factor) {
return x -> x * factor; // 返回函数
}
// C++ - 函数作为参数(使用 std::function)
#include <functional>
#include <vector>
void applyToAll(std::vector<int>& vec, std::function<int(int)> func) {
for (auto& x : vec) {
x = func(x);
}
}
std::vector<int> numbers = {1, 2, 3, 4};
applyToAll(numbers, [](int x) { return x * 2; }); // 传入 lambda
高阶函数通过参数化行为而非数据,实现了更灵活的代码复用。
结构化复用
结构化复用通过类、继承、组合、接口等机制,实现数据和行为的组织与复用。
类和对象
类(Class)封装数据和方法,对象(Object)是类的实例,实现了数据和行为的复用。
# Python
class Calculator:
def add(self, a, b):
return a + b
def multiply(self, a, b):
return a * b
calc = Calculator()
result = calc.add(1, 2)
// TypeScript
class Calculator {
add(a: number, b: number): number {
return a + b;
}
multiply(a: number, b: number): number {
return a * b;
}
}
const calc = new Calculator();
const result = calc.add(1, 2);
// Java
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public int multiply(int a, int b) {
return a * b;
}
}
Calculator calc = new Calculator();
int result = calc.add(1, 2);
// C++
class Calculator {
public:
int add(int a, int b) {
return a + b;
}
int multiply(int a, int b) {
return a * b;
}
};
Calculator calc;
int result = calc.add(1, 2);
继承
继承(Inheritance)允许子类复用父类的代码,并扩展或修改行为。
# Python
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof"
class Cat(Animal):
def speak(self):
return "Meow"
// TypeScript
class Animal {
speak(): string {
return "";
}
}
class Dog extends Animal {
speak(): string {
return "Woof";
}
}
// Java
class Animal {
public String speak() {
return "";
}
}
class Dog extends Animal {
@Override
public String speak() {
return "Woof";
}
}
// C++
class Animal {
public:
virtual std::string speak() {
return "";
}
};
class Dog : public Animal {
public:
std::string speak() override {
return "Woof";
}
};
接口(Interface)和协议(Protocol) 是定义类型契约的机制,通过多态(Polymorphism)支持代码复用。它们不直接包含可复用的代码,而是通过约定统一的方法签名,让不同类的实例可以被同一段代码处理。
- 接口:名义类型(Nominal Typing),类必须显式声明实现接口
- 协议:结构类型(Structural Typing),只要结构匹配即满足协议
# Python - 协议(结构类型)
from typing import Protocol
class Drawable(Protocol): # 协议
def draw(self) -> str:
... # 这里用 ... 或 pass 都可以,但不要有实际实现
class Circle:
def draw(self) -> str: # 无需显式声明实现协议
return "Drawing circle"
def render(shape: Drawable): # Circle 自动满足协议
print(shape.draw())
# 使用示例
circle = Circle()
render(circle) # 输出: Drawing circle
// TypeScript - 接口支持多态
interface Animal {
speak(): string;
}
class Dog implements Animal {
speak() {
return "Woof";
}
}
// 通过接口复用同一个函数
function makeSound(animal: Animal) {
console.log(animal.speak());
}
makeSound(new Dog()); // Woof
组合
组合(Composition)通过包含其他对象来复用功能,比继承更灵活。
# Python
class Engine:
def start(self):
return "Engine started"
class Car:
def __init__(self):
self.engine = Engine() # 组合
def start(self):
return self.engine.start()
// TypeScript
class Engine {
start(): string {
return "Engine started";
}
}
class Car {
private engine: Engine;
constructor() {
this.engine = new Engine(); // 组合
}
start(): string {
return this.engine.start();
}
}
// Java
class Engine {
public String start() {
return "Engine started";
}
}
class Car {
private Engine engine; // 组合
public Car() {
this.engine = new Engine();
}
public String start() {
return engine.start();
}
}
// C++
class Engine {
public:
std::string start() {
return "Engine started";
}
};
class Car {
private:
Engine engine; // 组合
public:
std::string start() {
return engine.start();
}
};
类型参数化复用
类型参数化通过类型参数编写适用于多种类型的代码,避免重复实现相似逻辑。不同语言采用不同的实现原理,各有权衡。
C++ - 编译期单态化(Monomorphization)
C++ 模板在编译期为每个类型实例生成独立的机器码,实现零运行时开销,但会增加二进制文件大小。
// C++ - 函数模板
template<typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
int i = max(3, 5); // 编译期生成 max<int> 的独立代码
double d = max(3.1, 5.2); // 编译期生成 max<double> 的独立代码
// C++ - 类模板
template<typename T>
class Stack {
private:
std::vector<T> elements;
public:
void push(T elem) {
elements.push_back(elem);
}
T pop() {
T elem = elements.back();
elements.pop_back();
return elem;
}
};
Stack<int> intStack; // 编译期生成 Stack<int> 的完整类代码
Stack<string> strStack; // 编译期生成 Stack<string> 的完整类代码
Java - 编译期类型检查,运行时擦除类型信息
Java 泛型在编译期进行类型检查,保证类型安全,但在运行时擦除泛型类型参数(如 List<String> 的 <String>),退化为原始类型。注意:虽然泛型信息被擦除,但对象本身仍是强类型的。
// Java - 泛型函数
public <T> T first(List<T> items) {
return items.get(0);
}
Integer num = first(Arrays.asList(1, 2, 3)); // 编译期检查类型
String text = first(Arrays.asList("a", "b")); // 运行时泛型 <T> 被擦除
// Java - 类型擦除示例
List<String> strings = new ArrayList<>();
strings.add("hello");
strings.add(123); // ❌ 编译期错误:类型不匹配,无法通过编译
// 运行时 List<String> 和 List<Integer> 是同一个类(都是 ArrayList)
String s = strings.get(0); // ✅ 编译器自动插入 (String) 强制转换
// 可以通过原始类型绕过编译期检查(不推荐)
List rawList = strings; // 转换为原始类型
rawList.add(123); // ⚠️ 编译器警告,但允许通过
String wrong = strings.get(1); // ❌ 运行时 ClassCastException
// 原因:编译器插入的 (String) 转换失败,因为实际存储的是 Integer
TS/Python - 编译期类型检查,运行时动态类型
TypeScript 和 Python 的类型提示仅用于编译期(或静态分析期)的类型检查,运行时完全是动态类型,变量可以随时改变类型,类型信息不存在。
// TypeScript - 泛型函数
function first<T>(items: T[]): T {
return items[0];
}
const num = first([1, 2, 3]); // 编译期推断 T 为 number
const text = first(["a", "b"]); // 编译期推断 T 为 string
// 编译为 JavaScript 后,类型信息完全消失
// TypeScript - 类型检查 vs 运行时行为
let value: string = "hello";
value = 123; // ❌ TypeScript 编译错误,无法通过 tsc 编译
// 如果绕过类型检查(如使用 @ts-ignore 或直接写 JS):
// JavaScript 运行时:✅ 完全合法,变量可随时改变类型
# Python - 泛型提示
from typing import List
def first(items: List[str]) -> str:
return items[0]
result = first([1, 2, 3]) # ⚠️ 类型检查器警告,但运行时正常执行
# Python - 运行时动态类型
items: list[str] = ["hello"]
items.append(123) # ✅ 运行时允许,类型提示不影响执行
x: str = "hello"
x = 123 # ✅ 完全合法,变量可随时改变类型
宏
宏(Macro)在预处理阶段进行文本替换,实现代码复用。C、C++ 支持,Python、TypeScript、Java 不支持。
// C - 宏
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int result = MAX(3, 5); // 预处理后替换为 ((3) > (5) ? (3) : (5))
// C++ - 宏(用法与 C 相同)
#define SQUARE(x) ((x) * (x))
int result = SQUARE(5); // 预处理后替换为 ((5) * (5))
装饰器包装复用
装饰器(Decorator)通过包装现有函数或类来复用和增强功能,在不修改原代码的情况下添加额外行为。Python、TypeScript 支持,Java、C++ 不支持。
# Python - 装饰器
def timing(func):
def wrapper(*args, **kwargs):
import time
start = time.time()
result = func(*args, **kwargs)
print(f"Time: {time.time() - start}")
return result
return wrapper
@timing
def process():
import time
time.sleep(1)
process() # 输出执行时间
// TypeScript - 装饰器
function timing(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function (...args: any[]) {
const start = Date.now();
const result = original.apply(this, args);
console.log(`Time: ${Date.now() - start}ms`);
return result;
};
}
class Service {
@timing
process() {
// 执行逻辑
}
}
注解(Annotation)在写法上与装饰器相似(都使用 @ 符号),但本质不同:装饰器直接包装并修改函数/类的行为,而注解只是添加元数据标记,由编译器或框架在编译/运行时读取并处理。例如 Java 的 @Override 用于编译期检查,Spring 的 @Autowired 用于运行时依赖注入。
模块化复用
模块(Module)和包(Package)系统是跨文件、跨项目代码复用的基础,通过导入/导出机制实现代码共享。
# 导入整个模块
import math
print(math.sqrt(16)) # 4.0
# 导入特定函数
from datetime import datetime
print(datetime.now())
# 导入第三方库
import requests
response = requests.get('https://api.example.com')
// 导出函数和类
export function calculateTax(amount: number): number {
return amount * 0.1;
}
export class User {
constructor(public name: string) {}
}
// 导入使用
import { calculateTax, User } from "./utils";
import express from "express"; // 第三方库
// 声明包
package com.example.utils;
public class Calculator {
public static int add(int a, int b) {
return a + b;
}
}
// 导入使用
import com.example.utils.Calculator;
import java.util.ArrayList; // 标准库
import java.util.*; // 通配符导入
// 标准库头文件
#include <vector>
#include <string>
#include <iostream>
// 自定义头文件
#include "myheader.h"
// C++20 模块(现代方式)
import std.core;
import mymodule;
模块系统是代码复用的基础设施,所有其他复用机制都依赖于模块系统来组织和分发代码。