理解OCP开闭原则和DIP依赖倒置原则

1.1 OCP开闭原则

首先以一个例子引入,传统方法,不使用遵循OCP开闭原则的弊端

代码结构:

理解OCP开闭原则和DIP依赖倒置原则_第1张图片

public interface UserDaoImpl {
    /*返回用户ID类型*/
    String getUserType(String userId);
}


public class UserDao implements UserDaoImpl {
    @Override
    public String getUserType(String userId) {
        // 假设这里根据用户 ID 返回用户类型
        if ("user1".equals(userId)) {
            return "Admin";
        } else if ("user2".equals(userId)) {
            return "Member";
        }
        return "Guest";
    }
}
public interface UserServiceImpl {
    void printUserDetails(String userId);
}

public class UserService implements UserServiceImpl {
    private UserDaoImpl userDaoImpl = new UserDao();


    @Override
    public void printUserDetails(String userId) {
        String userType = userDaoImpl.getUserType(userId);
        // 根据用户类型打印不同的信息
        if ("Admin".equals(userType)) {
            System.out.println("Welcome, Admin! You have full access.");
        } else if ("Member".equals(userType)) {
            System.out.println("Welcome, Member! You have limited access.");
        } else {
            System.out.println("Welcome, Guest! Please register to access more features.");
        }
    }
}
public class UserDaoController {

    private UserServiceImpl userServiceImpl = new UserService();

    public void handleRequest(String userId) {
        userServiceImpl.printUserDetails(userId);
    }
}
public class Main {
    public static void main(String[] args) {
        UserDaoController controller = new UserDaoController();
        controller.handleRequest("user1"); // 输出: Welcome, Admin! You have full access.
        controller.handleRequest("user2"); // 输出: Welcome, Member! You have limited access.
        controller.handleRequest("user3"); // 输出: Welcome, Guest! Please register to access more features.
    }
}
违反 OCP 的原因分析

问题:

  1. 修改现有代码以扩展功能
    • 如果需要新增一种用户类型(如 "VIP" 用户),必须修改 UserService 中的 printUserDetails 方法,添加新的 if-else 分支。
if ("VIP".equals(userType)) {
    System.out.println("Welcome, VIP! Enjoy exclusive benefits.");
}
    • 这种做法违反了 OCP,因为每次新增需求都需要修改现有代码
  1. 紧耦合
    • UserService 直接依赖于具体的用户类型逻辑(如 "Admin""Member"),导致代码难以扩展和维护。
    • 新增或修改用户类型时,可能引入错误或破坏现有功能。

那么如何去解决这个问题呢。

我们可以通过引入 策略模式 抽象接口 来改进代码,使其符合 OCP。以下是改进后的代码:

// 定义用户行为接口
public interface UserBehavior {
    void printMessage();
}

// 不同用户类型的实现
public class AdminBehavior implements UserBehavior {
    @Override
    public void printMessage() {
        System.out.println("Welcome, Admin! You have full access.");
    }
}

public class MemberBehavior implements UserBehavior {
    @Override
    public void printMessage() {
        System.out.println("Welcome, Member! You have limited access.");
    }
}

public class GuestBehavior implements UserBehavior {
    @Override
    public void printMessage() {
        System.out.println("Welcome, Guest! Please register to access more features.");
    }
}

// 工厂类:根据用户类型返回对应的实现
public class UserBehaviorFactory {
    public static UserBehavior getBehavior(String userType) {
        switch (userType) {
            case "Admin":
                return new AdminBehavior();
            case "Member":
                return new MemberBehavior();
            case "Guest":
                return new GuestBehavior();
            default:
                throw new IllegalArgumentException("Unknown user type: " + userType);
        }
    }
}

// 修改后的 Service 层
public class UserService {
    private UserDao userDao = new UserDao();

    public void printUserDetails(String userId) {
        String userType = userDao.getUserType(userId);

        // 使用工厂类获取对应的行为实现
        UserBehavior behavior = UserBehaviorFactory.getBehavior(userType);
        behavior.printMessage();
    }
}

// 其他层保持不变
public class UserController {
    private UserService userService = new UserService();

    public void handleRequest(String userId) {
        userService.printUserDetails(userId);
    }
}

// 测试类
public class Main {
    public static void main(String[] args) {
        UserController controller = new UserController();
        controller.handleRequest("user1"); // 输出: Welcome, Admin! You have full access.
        controller.handleRequest("user2"); // 输出: Welcome, Member! You have limited access.
        controller.handleRequest("user3"); // 输出: Welcome, Guest! Please register to access more features.
    }
}

但上面代码还不是很成功,当用户类型增加,那么工厂类是不是又要继续去switch...case了呢,此时我们只是提出传统写法是违反OCP开闭原则的

1.2 DIP依赖倒置原则

  • 什么是依赖倒置原则?
    面向接口编程,面向抽象编程,不要面向具体编程。
    • 依赖倒置原则的目的?
      降低程序的耦合度,提高扩展力。
    • 什么叫做符合依赖倒置?
      上 不依赖 下,就是符合。
    • 什么叫做违背依赖倒置?
      上 依赖 下,就是违背。
      只要“下”一改动,“上”就受到牵连。
  • 从上面的代码可以看出业务层service代码需要依赖Dao层的代码,需要创建该对象,Controller层又要依赖service层的代码,可以得到其耦合程度太高了,
  • 违反DIP规则
public class MySQLDatabase {
    public void save(String data) {
        System.out.println("Saving data to MySQL: " + data);
    }
}

public class UserService {
    private MySQLDatabase database;

    public UserService() {
        this.database = new MySQLDatabase();
    }

    public void saveUser(String user) {
        database.save(user);
    }
}

问题此时就会出现

    • UserService 直接依赖于 MySQLDatabase,如果未来需要切换到其他数据库(如 MongoDB),就需要修改 UserService 的代码。
    • 违反了 DIP,因为高层模块(UserService)依赖于低层模块(MySQLDatabase)的具体实现。
遵循 DIP 的设计

通过引入一个抽象接口 Database,使 UserServiceMySQLDatabase 都依赖于这个接口:

// 抽象接口
public interface Database {
    void save(String data);
}

// 具体实现
public class MySQLDatabase implements Database {
    @Override
    public void save(String data) {
        System.out.println("Saving data to MySQL: " + data);
    }
}

public class MongoDBDatabase implements Database {
    @Override
    public void save(String data) {
        System.out.println("Saving data to MongoDB: " + data);
    }
}

// 高层模块
public class UserService {
    private Database database;

    public UserService(Database database) {
        this.database = database;
    }

    public void saveUser(String user) {
        database.save(user);
    }
}

这样子就可以实现,当我去在调用时,总结使用多态传递相关的子类引用,那么就可以达到后期增加业务时,加代码,总结创建类,实现接口即可,不会去修改原有代码。

你可能感兴趣的:(java,开发语言,依赖倒置原则,开闭原则)