设计模式的七大原则是面向对象设计的核心,它们是创建高质量、可维护性代码的基石。以下是这七大原则及其概念和例子:
1. 单一职责原则 (Single Responsibility Principle, SRP)
概念:一个类应该只有一个变化的原因。这意味着一个类应该只包含与某个单一功能相关的代码。
例子:假设我们有一个 UserService
类,它负责用户注册和用户登录。如果有一天,我们想要添加一个新功能来更新用户信息,这时 UserService
类就需要修改,因为它负责的功能发生了变化。为了遵循SRP,我们应该将这个类拆分为两个:UserRegistrationService
和 UserLoginService
。
// 不良设计,违反SRP
class UserService {
public void registerUser(String username, String password) {
// 注册用户逻辑
}
public void loginUser(String username, String password) {
// 登录用户逻辑
}
public void updateUserInfo(String username, String newInfo) {
// 更新用户信息逻辑
}
}
// 遵循SRP的设计
class UserRegistrationService {
public void registerUser(String username, String password) {
// 注册用户逻辑
}
}
class UserLoginService {
public boolean loginUser(String username, String password) {
// 登录用户逻辑
return true; // 假设登录成功
}
}
class UserUpdateService {
public void updateUserInfo(String username, String newInfo) {
// 更新用户信息逻辑
}
}
2. 开闭原则 (Open/Closed Principle, OCP)
概念:软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。这意味着在不修改现有代码的情况下,应该能扩展一个软件实体以添加新的功能。
例子:假设有一个 Shape
接口,它有一个 draw
方法。现在,我们有一个 Rectangle
类和 Circle
类实现了这个接口。如果我们想要添加一个新的形状 Triangle
,我们应该通过扩展而不是修改现有类来实现。
// 遵循OCP的设计
abstract class Shape {
public abstract void draw();
}
class Rectangle extends Shape {
public void draw() {
System.out.println("Drawing Rectangle");
}
}
class Circle extends Shape {
public void draw() {
System.out.println("Drawing Circle");
}
}
// 假设我们想要添加一个新的形状Triangle
class Triangle extends Shape {
public void draw() {
System.out.println("Drawing Triangle");
}
}
3. 里氏替换原则 (Liskov Substitution Principle, LSP)
概念:所有引用基类(父类)的地方都可以透明地使用其子类的对象。这意味着子类可以替换基类,而不会改变程序的行为。
例子:假设有一个 Animal
类,它有一个 move
方法。我们有一个子类 Dog
,它覆盖了 move
方法。在任何使用 Animal
的地方,我们都可以使用 Dog
,而程序的行为不会改变。
// 遵循LSP的设计
class Animal {
public void move() {
System.out.println("Animal is moving");
}
}
class Dog extends Animal {
public void move() {
System.out.println("Dog is running");
}
}
// LSP允许子类替换基类
class Zoo {
public void animalMovement(Animal animal) {
animal.move();
}
}
public class Main {
public static void main(String[] args) {
Zoo zoo = new Zoo();
Animal animal = new Dog();
zoo.animalMovement(animal); // Dog is running
}
}
4. 接口隔离原则 (Interface Segregation Principle, ISP)
概念:使用多个专门的接口比使用单一的总接口要好。这意味着接口应该小而精,只提供客户端实际需要的功能。
例子:假设有一个 Animal
类,它实现了 eat
、sleep
和 move
三个方法。如果我们有一个专门处理睡眠的类,它不需要 eat
和 move
方法,那么我们应该创建一个只包含 sleep
方法的接口。
// 遵循ISP的设计
interface EatBehavior {
void eat();
}
interface SleepBehavior {
void sleep();
}
interface Animal {
EatBehavior getEatBehavior();
SleepBehavior getSleepBehavior();
}
class Cat implements Animal {
private EatBehavior eatBehavior;
private SleepBehavior sleepBehavior;
public Cat() {
eatBehavior = new EatBehavior() {
public void eat() {
System.out.println("Cat is eating");
}
};
sleepBehavior = new SleepBehavior() {
public void sleep() {
System.out.println("Cat is sleeping");
}
};
}
public EatBehavior getEatBehavior() {
return eatBehavior;
}
public SleepBehavior getSleepBehavior() {
return sleepBehavior;
}
}
5. 依赖倒置原则 (Dependency Inversion Principle, DIP)
概念:高层模块不应该依赖低层模块,两者都应该依赖抽象。抽象不应该依赖细节,细节应该依赖抽象。
例子:假设有一个 UserService
类,它直接依赖 UserDao
类。为了遵循DIP,我们应该将 UserService
类改为依赖一个 UserRepository
接口,然后 UserDao
类实现这个接口。
// 遵循DIP的设计
interface UserRepository {
void saveUser(User user);
User findUserById(int id);
}
class UserDao implements UserRepository {
public void saveUser(User user) {
// 假设的保存用户逻辑
}
public User findUserById(int id) {
// 假设的根据ID查找用户逻辑
return new User();
}
}
class UserService {
private UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void registerUser(String username, String password) {
User user = new User(username, password);
userRepository.saveUser(user);
}
}
6. 迪米特法则 (Law of Demeter, LoD)
概念:一个软件实体应当尽量少的与其他实体发生相互作用。这意味着我们应该尽量减少类之间的耦合,使得类更加模块化。
例子:假设有一个 OrderService
类,它需要访问 Customer
类和 ShippingAddress
类。为了遵循LoD,我们应该尽量减少 OrderService
类与其他类的交互,可以将一些数据封装在内部类或使用组合。
// 遵循LoD的设计
class OrderService {
private CustomerService customerService;
private ShippingAddressService shippingAddressService;
public OrderService(CustomerService customerService, ShippingAddressService shippingAddressService) {
this.customerService = customerService;
this.shippingAddressService = shippingAddressService;
}
public void processOrder(Order order) {
// 假设的订单处理逻辑
Customer customer = customerService.getCustomerByName(order.getCustomerName());
ShippingAddress shippingAddress = shippingAddressService.getShippingAddress(customer);
// 订单处理逻辑,使用customer和shippingAddress
}
}
class CustomerService {
public Customer getCustomerByName(String customerName) {
// 假设的根据名字查找客户逻辑
return new Customer();
}
}
class ShippingAddressService {
public ShippingAddress getShippingAddress(Customer customer) {
// 假设的根据客户获取配送地址逻辑
return new ShippingAddress();
}
}
7. 合成/聚合复用原则 (Composite/Aggregation Reuse Principle, CARP)
概念:尽量使用组合/聚合关系来达到代码复用的目的,而不是使用继承。
例子:假设有一个 Order
类,它包含一个 LineItem
列表。在这种情况下,我们不应该创建一个 LineItem
子类来表示不同的订单行项目,而应该使用组合关系,将不同的订单行项目作为 Order
类的成员对象。
// 遵循CARP的设计
class Order {
private List<LineItem> lineItems;
public Order() {
this.lineItems = new ArrayList<>();
}
public void addLineItem(LineItem lineItem) {
lineItems.add(lineItem);
}
public double calculateTotal() {
double total = 0;
for (LineItem item : lineItems) {
total += item.getCost();
}
return total;
}
}
class LineItem {
private String name;
private double price;
public LineItem(String name, double price) {
this.name = name;
this.price = price;
}
public double getCost() {
return price;
}
}
总结
设计模式是软件设计中的一种经验总结,它们可以用来解决常见的编程问题,提高代码的质量。然而,设计模式并不是银弹,它们也需要根据实际情况来合理使用。
正确地使用设计模式可以提高代码的可维护性和可扩展性,但错误地使用可能会增加代码复杂度、学习成本和性能开销。还是那句话,设计模式只是一个工具,不要为了使用设计模式而用。