面向对象的设计原则
面向对象的设计原则是编写可维护、可扩展、可复用软件的基础。以下是主要的面向对象设计原则:
1. SOLID 原则
1.1 单一职责原则 (SRP)
一个类应该只有一个引起变化的原因
// 违反 SRP
class User {
private String name;
private String email;
public void saveToDatabase() {
// 数据库操作
}
public void sendEmail() {
// 发送邮件
}
public void validate() {
// 验证逻辑
}
}
// 遵循 SRP
class User {
private String name;
private String email;
// 只包含用户核心属性
}
class UserRepository {
public void save(User user) {
// 数据库操作
}
}
class EmailService {
public void sendEmail(User user) {
// 发送邮件
}
}
class UserValidator {
public boolean validate(User user) {
// 验证逻辑
}
}
1.2 开闭原则 (OCP)
对扩展开放,对修改关闭
// 违反 OCP
class ShapeDrawer {
public void draw(String shapeType) {
if (shapeType.equals("circle")) {
drawCircle();
} else if (shapeType.equals("rectangle")) {
drawRectangle();
}
// 添加新形状需要修改此类
}
}
// 遵循 OCP
interface Shape {
void draw();
}
class Circle implements Shape {
@Override
public void draw() {
// 绘制圆形
}
}
class Rectangle implements Shape {
@Override
public void draw() {
// 绘制矩形
}
}
class ShapeDrawer {
public void draw(Shape shape) {
shape.draw(); // 无需修改即可支持新形状
}
}
// 扩展新形状
class Triangle implements Shape {
@Override
public void draw() {
// 绘制三角形
}
}
1.3 里氏替换原则 (LSP)
子类必须能够替换它们的父类
// 违反 LSP
class Rectangle {
protected int width;
protected int height;
public void setWidth(int width) {
this.width = width;
}
public void setHeight(int height) {
this.height = height;
}
public int getArea() {
return width * height;
}
}
class Square extends Rectangle {
@Override
public void setWidth(int width) {
super.setWidth(width);
super.setHeight(width); // 破坏了矩形行为
}
@Override
public void setHeight(int height) {
super.setHeight(height);
super.setWidth(height); // 破坏了矩形行为
}
}
// 测试代码
void testArea(Rectangle rectangle) {
rectangle.setWidth(5);
rectangle.setHeight(4);
assert rectangle.getArea() == 20; // 对于 Square 会失败
}
// 遵循 LSP
abstract class Shape {
public abstract int getArea();
}
class Rectangle extends Shape {
private int width;
private int height;
// getters and setters
@Override
public int getArea() {
return width * height;
}
}
class Square extends Shape {
private int side;
public void setSide(int side) {
this.side = side;
}
@Override
public int getArea() {
return side * side;
}
}
1.4 接口隔离原则 (ISP)
客户端不应该被迫依赖它们不使用的接口
// 违反 ISP
interface Worker {
void work();
void eat();
void sleep();
}
class HumanWorker implements Worker {
@Override
public void work() { /* 工作 */ }
@Override
public void eat() { /* 吃饭 */ }
@Override
public void sleep() { /* 睡觉 */ }
}
class RobotWorker implements Worker {
@Override
public void work() { /* 工作 */ }
@Override
public void eat() {
throw new UnsupportedOperationException("机器人不需要吃饭");
}
@Override
public void sleep() {
throw new UnsupportedOperationException("机器人不需要睡觉");
}
}
// 遵循 ISP
interface Workable {
void work();
}
interface Eatable {
void eat();
}
interface Sleepable {
void sleep();
}
class HumanWorker implements Workable, Eatable, Sleepable {
@Override
public void work() { /* 工作 */ }
@Override
public void eat() { /* 吃饭 */ }
@Override
public void sleep() { /* 睡觉 */ }
}
class RobotWorker implements Workable {
@Override
public void work() { /* 工作 */ }
}
1.5 依赖倒置原则 (DIP)
依赖抽象而不是具体实现
// 违反 DIP
class LightBulb {
public void turnOn() {
// 开灯
}
public void turnOff() {
// 关灯
}
}
class Switch {
private LightBulb bulb;
public Switch() {
this.bulb = new LightBulb(); // 依赖具体类
}
public void operate() {
// 操作灯泡
bulb.turnOn();
}
}
// 遵循 DIP
interface Switchable {
void turnOn();
void turnOff();
}
class LightBulb implements Switchable {
@Override
public void turnOn() {
// 开灯
}
@Override
public void turnOff() {
// 关灯
}
}
class Fan implements Switchable {
@Override
public void turnOn() {
// 开风扇
}
@Override
public void turnOff() {
// 关风扇
}
}
class Switch {
private Switchable device;
public Switch(Switchable device) { // 依赖抽象
this.device = device;
}
public void operate() {
device.turnOn();
}
}
2. 其他重要原则
2.1 组合优于继承原则
优先使用组合而不是继承来复用代码
// 使用继承(不推荐)
class Bird {
public void fly() {
// 飞行逻辑
}
}
class Penguin extends Bird {
// 企鹅不会飞,但继承了 fly 方法
@Override
public void fly() {
throw new UnsupportedOperationException("企鹅不会飞");
}
}
// 使用组合(推荐)
interface Flyable {
void fly();
}
class FlyingAbility implements Flyable {
@Override
public void fly() {
// 飞行逻辑
}
}
class Bird {
protected Flyable flyAbility;
public Bird(Flyable flyAbility) {
this.flyAbility = flyAbility;
}
public void performFly() {
if (flyAbility != null) {
flyAbility.fly();
}
}
}
class Sparrow extends Bird {
public Sparrow() {
super(new FlyingAbility());
}
}
class Penguin extends Bird {
public Penguin() {
super(null); // 没有飞行能力
}
}
2.2 迪米特法则(最少知识原则)
一个对象应该对其他对象有最少的了解
// 违反迪米特法则
class Customer {
private Wallet wallet;
public Wallet getWallet() {
return wallet;
}
}
class Paperboy {
public void charge(Customer customer, int amount) {
Wallet wallet = customer.getWallet(); // 知道 Customer 有 Wallet
if (wallet.getMoney() >= amount) {
wallet.subtractMoney(amount);
}
}
}
// 遵循迪米特法则
class Customer {
private Wallet wallet;
public boolean pay(int amount) {
return wallet.subtractMoneyIfEnough(amount);
}
}
class Paperboy {
public void charge(Customer customer, int amount) {
customer.pay(amount); // 只与 Customer 交互
}
}
2.3 不要重复你自己 (DRY)
避免代码重复
// 违反 DRY
class UserService {
public void createUser(String username, String email) {
if (username == null || username.trim().isEmpty()) {
throw new IllegalArgumentException("用户名不能为空");
}
if (email == null || !email.contains("@")) {
throw new IllegalArgumentException("邮箱格式错误");
}
// 创建用户逻辑
}
public void updateUser(String username, String email) {
if (username == null || username.trim().isEmpty()) {
throw new IllegalArgumentException("用户名不能为空");
}
if (email == null || !email.contains("@")) {
throw new IllegalArgumentException("邮箱格式错误");
}
// 更新用户逻辑
}
}
// 遵循 DRY
class Validator {
public static void validateUsername(String username) {
if (username == null || username.trim().isEmpty()) {
throw new IllegalArgumentException("用户名不能为空");
}
}
public static void validateEmail(String email) {
if (email == null || !email.contains("@")) {
throw new IllegalArgumentException("邮箱格式错误");
}
}
}
class UserService {
public void createUser(String username, String email) {
Validator.validateUsername(username);
Validator.validateEmail(email);
// 创建用户逻辑
}
public void updateUser(String username, String email) {
Validator.validateUsername(username);
Validator.validateEmail(email);
// 更新用户逻辑
}
}
3. 实际应用示例
3.1 电商系统设计
// 遵循 SOLID 原则的电商系统
interface PaymentMethod {
void pay(double amount);
}
class CreditCardPayment implements PaymentMethod {
@Override
public void pay(double amount) {
// 信用卡支付逻辑
}
}
class PayPalPayment implements PaymentMethod {
@Override
public void pay(double amount) {
// PayPal 支付逻辑
}
}
interface NotificationService {
void sendNotification(String message);
}
class EmailNotification implements NotificationService {
@Override
public void sendNotification(String message) {
// 邮件通知逻辑
}
}
class SMSNotification implements NotificationService {
@Override
public void sendNotification(String message) {
// 短信通知逻辑
}
}
class OrderProcessor {
private PaymentMethod paymentMethod;
private NotificationService notificationService;
public OrderProcessor(PaymentMethod paymentMethod,
NotificationService notificationService) {
this.paymentMethod = paymentMethod;
this.notificationService = notificationService;
}
public void processOrder(Order order) {
paymentMethod.pay(order.getTotalAmount());
notificationService.sendNotification("订单处理完成");
}
}
3.2 设计原则检查清单
| 原则 | 检查问题 |
|---|---|
| SRP | 这个类是否只有一个职责? |
| OCP | 添加新功能是否需要修改现有代码? |
| LSP | 子类是否能完全替换父类? |
| ISP | 接口是否足够小和专注? |
| DIP | 是否依赖抽象而不是具体实现? |
| 组合 | 是否过度使用继承? |
| 迪米特 | 对象是否知道太多其他对象? |
| DRY | 是否有重复的代码逻辑? |
4. 设计原则的权衡
在实际项目中,需要根据具体情况权衡这些原则:
- 过度设计 vs 适度设计:不要为了原则而原则
- 项目阶段考虑:原型阶段可以适当放宽,核心系统要严格遵循
- 团队技能:根据团队经验选择合适的设计复杂度
- 性能要求:某些原则可能带来性能开销
// 适度设计的例子
// 对于小型项目,可以适当合并职责
class SimpleUserService {
// 合并了用户相关的多个职责,但代码更简单
public void register(User user) { }
public void login(String username, String password) { }
public void resetPassword(String email) { }
}
// 对于大型企业系统,应该严格分离
interface UserRegistration { }
interface UserAuthentication { }
interface PasswordManagement { }
掌握这些设计原则能够帮助你创建更加健壮、可维护的面向对象系统。关键在于理解原则背后的思想,而不是机械地套用。
挣钱养家

浙公网安备 33010602011771号