项目需求分析与设计

定义系统功能和架构设计

需求分析步骤

1

确定业务需求

与客户沟通,明确系统需要解决的问题和核心功能点

常见错误

在没有明确需求的情况下直接开始编码,导致后期频繁修改

2

创建用例图

使用UML描述系统功能和用户交互

// 示例:用户管理模块用例
Actor: 管理员
Use Cases:
- 添加用户
- 删除用户
- 修改用户信息
- 查询用户列表

Actor: 普通用户
Use Cases:
- 查看个人信息
- 修改密码
- 退出登录

三层架构设计

表现层 (UI)

控制台/Swing界面

用户交互处理

业务逻辑层 (Service)

业务规则实现

事务管理

数据访问层 (DAO)

数据库操作

持久化逻辑

数据库设计与实现

设计数据库模型并实现SQL脚本

数据库设计步骤

1

实体关系建模

识别系统中的实体、属性及关系

// 用户实体
User {
    id: INT (PK)
    username: VARCHAR(50)
    password: VARCHAR(100)
    email: VARCHAR(100)
    created_at: TIMESTAMP
}

// 订单实体
Order {
    id: INT (PK)
    user_id: INT (FK to User.id)
    total_price: DECIMAL(10,2)
    status: ENUM('CREATED','PAID','SHIPPED')
}
2

SQL脚本实现

创建DDL脚本定义数据库表结构

CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL UNIQUE,
    password VARCHAR(100) NOT NULL,
    email VARCHAR(100) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE orders (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT NOT NULL,
    total_price DECIMAL(10,2) NOT NULL,
    status ENUM('CREATED','PAID','SHIPPED') DEFAULT 'CREATED',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id)
);

数据库设计原则

1. 遵循数据库三大范式

2. 主键使用自增整数或UUID

3. 为常用查询字段创建索引

4. 使用外键维护数据一致性

5. 避免过度设计,保持结构简单

数据访问层开发(DAO)

实现数据库操作接口

DAO模式实现

1

定义DAO接口

使用接口声明数据库操作方法

public interface UserDao {
                        // 添加用户
                        void addUser(User user);
    
                        // 根据ID查找用户
    User getUserById(int id);
    
                        // 更新用户信息
                        void updateUser(User user);
    
                        // 删除用户
                        void deleteUser(int id);
    
                        // 获取所有用户
    List<User> getAllUsers();
}
2

JDBC实现DAO

使用JDBC实现数据访问逻辑

public class UserDaoJdbcImpl implements UserDao {
                        private final Connection connection;
    
                        public UserDaoJdbcImpl(Connection connection) {
                        this.connection = connection;
    }

                        @Override
                        public User getUserById(int id) {
        String sql = "SELECT * FROM users WHERE id = ?";
                        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            stmt.setInt(1, id);
            ResultSet rs = stmt.executeQuery();
                        if (rs.next()) {
                        return new User(
                    rs.getInt("id"),
                    rs.getString("username"),
                    rs.getString("email")
                );
            }
        } catch (SQLException e) {
                        throw new RuntimeException("数据库查询失败", e);
        }
                        return null;
    }
    
                        // 其他方法实现...
}

DAO层最佳实践

1. 使用PreparedStatement防止SQL注入

2. 确保关闭所有数据库资源(Connection, Statement, ResultSet)

3. 使用连接池管理数据库连接

4. 将SQL异常转换为业务异常

5. 考虑使用Spring JDBCTemplate简化JDBC代码

业务逻辑层开发(Service)

实现核心业务逻辑

Service层实现

1

定义服务接口

抽象业务方法供控制器调用

public interface UserService {
                        // 用户注册
                        boolean register(User user, String password);
    
                        // 用户登录
    User login(String username, String password);
    
                        // 更新用户信息
                        void updateProfile(User user);
    
                        // 密码重置
                        void resetPassword(int userId, String newPassword);
}
2

实现服务逻辑

协调多个DAO操作,实现业务规则

public class UserServiceImpl implements UserService {
                        private final UserDao userDao;
    
                        public UserServiceImpl(UserDao userDao) {
                        this.userDao = userDao;
    }

                        @Override
                        public boolean register(User user, String password) {
                        // 检查用户名是否已存在
                        if (userDao.findByUsername(user.getUsername()) != null) {
                        throw new UserException("用户名已被使用");
        }
        
                        // 密码加密
        String hashedPassword = hashPassword(password);
        user.setPassword(hashedPassword);
        
                        // 保存用户
                        return userDao.save(user) > 0;
    }
    
                        private String hashPassword(String password) {
                        // 使用BCrypt加密
                        return BCrypt.hashpw(password, BCrypt.gensalt());
    }
    
                        // 其他方法实现...
}

常见错误:事务问题

服务方法中多个DAO操作应该放在同一个事务中:

// 错误:缺少事务管理
public void transferMoney(int fromId, int toId, BigDecimal amount) {
    accountDao.deductBalance(fromId, amount); // 可能失败
    accountDao.addBalance(toId, amount);   // 可能失败导致数据不一致
}

// 正确:添加事务管理
@Transactional  // 使用Spring声明式事务
public void transferMoney(int fromId, int toId, BigDecimal amount) {
    accountDao.deductBalance(fromId, amount);
    accountDao.addBalance(toId, amount);
}

用户界面开发

控制台和Swing界面实现

UI开发模式

1

控制台界面

实现基于文本的交互界面

public class ConsoleUI {
                            private final UserService userService;
    
                            public ConsoleUI(UserService userService) {
                            this.userService = userService;
    }
    
                            public void start() {
        Scanner scanner = new Scanner(System.in);
                            boolean running = true;
        
                            while (running) {
            System.out.println("1. 登录");
            System.out.println("2. 注册");
            System.out.println("3. 退出");
            System.out.print("请选择操作: ");
            
                            int choice = scanner.nextInt();
            scanner.nextLine(); // 消耗换行符
            
                            switch (choice) {
                            case 1: 
                            loginProcess(scanner);
                            break;
                            case 2: 
                            registerProcess(scanner);
                            break;
                            case 3: 
                    running = false;
                            break;
            }
        }
    }
    
                            private void loginProcess(Scanner scanner) {
                            // 获取用户名和密码并调用服务
    }
    
                            private void registerProcess(Scanner scanner) {
                            // 获取用户信息并调用注册服务
    }
}
2

Swing界面

实现图形用户界面

public class LoginFrame extends JFrame {
                            private final UserService userService;
                            private JTextField usernameField;
                            private JPasswordField passwordField;
    
                            public LoginFrame(UserService userService) {
                            this.userService = userService;
                            initUI();
    }
    
                            private void initUI() {
                            setTitle("用户登录");
                            setSize(300, 200);
                            setDefaultCloseOperation(EXIT_ON_CLOSE);
        
        JPanel panel = new JPanel(new GridLayout(3, 2, 5, 5));
        
        panel.add(new JLabel("用户名:"));
        usernameField = new JTextField();
        panel.add(usernameField);
        
        panel.add(new JLabel("密码:"));
        passwordField = new JPasswordField();
        panel.add(passwordField);
        
        JButton loginButton = new JButton("登录");
        loginButton.addActionListener(this::onLogin);
        panel.add(loginButton);
        
        JButton registerButton = new JButton("注册");
        registerButton.addActionListener(e -> openRegisterFrame());
        panel.add(registerButton);
        
                            add(panel);
    }
    
                            private void onLogin(ActionEvent e) {
        String username = usernameField.getText();
        String password = new String(passwordField.getPassword());
        
                            try {
            User user = userService.login(username, password);
                            // 登录成功处理
        } catch (UserException ex) {
            JOptionPane.showMessageDialog(this, ex.getMessage(), 
                            "登录失败", JOptionPane.ERROR_MESSAGE);
        }
    }
}

功能整合与测试

集成各组件并进行系统测试

测试方法与策略

单元测试(JUnit)

测试单个方法或类的功能

public class UserServiceTest {
                            private UserService userService;
                            private UserDao userDao;
    
                            @Before
                            public void setUp() {
                            // 使用Mockito模拟DAO对象
        userDao = Mockito.mock(UserDao.class);
        userService = new UserServiceImpl(userDao);
    }
    
                            @Test
                            public void testLoginSuccess() {
                            // 准备测试数据
        String username = "testuser";
        String password = "validPassword";
        String hashedPassword = BCrypt.hashpw(password, BCrypt.gensalt());
        
                            // 配置模拟对象行为
        Mockito.when(userDao.findByUsername(username))
            .thenReturn(new User(1, username, hashedPassword));
        
                            // 执行测试
        User user = userService.login(username, password);
        
                            // 验证结果
        Assert.assertNotNull(user);
        Assert.assertEquals(username, user.getUsername());
    }
}

集成测试

测试组件协同工作

public class UserIntegrationTest {
                            private static DataSource dataSource;
                            private UserService userService;
    
                            @BeforeClass
                            public static void setupDataSource() {
                            // 初始化测试数据库
        dataSource = createTestDataSource();
    }
    
                            @Before
                            public void init() {
                            // 创建DAO和服务实例
        UserDao userDao = new UserDaoJdbcImpl(dataSource);
        userService = new UserServiceImpl(userDao);
    }
    
                            @Test
                            public void testRegisterAndLogin() {
                            // 注册新用户
        User newUser = new User("newuser", "new@example.com");
        userService.register(newUser, "password123");
        
                            // 登录验证
        User loggedIn = userService.login("newuser", "password123");
        
                            // 验证登录结果
        Assert.assertNotNull(loggedIn);
        Assert.assertEquals("new@example.com", loggedIn.getEmail());
    }
}

功能整合流程

将各层组件组合成完整应用

public class ApplicationMain {
                        public static void main(String[] args) {
                        // 1. 初始化数据源
        DataSource dataSource = setupDataSource();
        
                        // 2. 创建DAO层
        UserDao userDao = new UserDaoJdbcImpl(dataSource);
        
                        // 3. 创建Service层
        UserService userService = new UserServiceImpl(userDao);
        
                        // 4. 启动控制台UI
                        if ("console".equals(args[0])) {
            ConsoleUI consoleUI = new ConsoleUI(userService);
            consoleUI.start();
        } 
                        // 5. 或启动Swing UI
                        else if ("swing".equals(args[0])) {
            SwingUtilities.invokeLater(() -> {
                LoginFrame frame = new LoginFrame(userService);
                frame.setVisible(true);
            });
        }
    }
}

测试最佳实践

1. 测试金字塔:编写大量单元测试,适量集成测试,少量端到端测试

2. 使用内存数据库(如H2)替代生产数据库进行测试

3. 使用Mock对象隔离测试单元

4. 自动化测试(如使用Jenkins、GitHub Actions)

5. 测试覆盖率分析(使用JaCoCo等工具)

总结与常见错误

项目回顾与经验总结

分层架构优势

  • 分离关注点,代码清晰
  • 模块解耦,便于维护
  • 各层可独立替换实现
  • 便于团队协作分工

常见错误列表

  • 数据库设计不合理,后期频繁修改表结构
  • 未进行参数验证导致SQL注入或业务逻辑错误
  • 事务处理不当导致数据不一致
  • UI层包含业务逻辑
  • 未进行足够的异常处理

进阶学习路径

  • Spring Framework核心(IoC, AOP)
  • Spring Boot快速开发
  • Spring Data JPA替代传统DAO
  • JavaFX现代化UI开发
  • 容器化部署(Docker, Kubernetes)

综合练习

实现一个图书馆管理系统,包括:

  • 数据库设计(读者、图书、借阅记录)
  • DAO层实现CRUD操作
  • Service层处理借阅/归还业务规则
  • 控制台和Swing双界面
  • 单元测试覆盖核心业务

提示: 考虑使用SQLite作为嵌入式数据库简化部署