12.3 实验二十一观察者模式 实验二十二状态模式 实验二十三策略模式

实验 21:观察者模式
本次实验属于模仿型实验,通过本次实验学生将掌握以下内容:
1、理解观察者模式的动机,掌握该模式的结构;
2、能够利用观察者模式解决实际问题。

[实验任务一]:股票提醒
当股票的价格上涨或下降5%时,会通知持有该股票的股民,当股民听到价格上涨的消息时会买股票,当价格下降时会大哭一场。
实验要求:
1.画出对应类图;
2.提交源代码;
3.注意编程规范。
image

StockSubject.java
import java.util.ArrayList;import java.util.List;
public abstract class StockSubject {
    // 维护观察者(股民)列表
    protected List<Investor> observers = new ArrayList<>();

    /**
     * 注册观察者(股民订阅股票)
     * @param investor 要注册的股民
     */
    public void registerObserver(Investor investor) {
        observers.add(investor);
    }

    /**
     * 移除观察者(股民取消订阅)
     * @param investor 要移除的股民
     */
    public void removeObserver(Investor investor) {
        observers.remove(investor);
    }

    /**
     * 通知所有观察者(股票价格变化触发)
     */
    public abstract void notifyObservers();}
2.ConcreteStock.java
public class ConcreteStock extends StockSubject {
    // 股票名称
    private final String stockName;
    // 当前价格
    private double price;
    // 上一次价格(用于计算变化率)
    private double lastPrice;

    /**
     * 构造方法:初始化股票名称和初始价格
     * @param stockName 股票名称
     * @param price 初始价格
     */
    public ConcreteStock(String stockName, double price) {
        this.stockName = stockName;
        this.price = price;
        this.lastPrice = price; // 初始时上一次价格=当前价格
    }

    /**
     * 设置股票价格,触发变化率检查和通知
     * @param newPrice 新价格
     */
    public void setPrice(double newPrice) {
        this.lastPrice = this.price; // 保存上一次价格
        this.price = newPrice;
        // 价格变化率≥5%时通知观察者
        if (Math.abs(getPriceChangeRate()) >= 5) {
            notifyObservers();
        }
    }

    /**
     * 计算价格变化率(百分比)
     * @return 变化率(正数=涨,负数=跌)
     */
    public double getPriceChangeRate() {
        return ((price - lastPrice) / lastPrice) * 100;
    }

    /**
     * 获取股票名称
     * @return 股票名称
     */
    public String getStockName() {
        return stockName;
    }

    /**
     * 获取当前价格
     * @return 当前价格
     */
    public double getPrice() {
        return price;
    }

    @Override
    public void notifyObservers() {
        boolean isRise = getPriceChangeRate() > 0; // 是否上涨
        for (Investor investor : observers) {
            investor.update(this, isRise); // 通知每个股民
        }
    }}
3. Investor.java
public interface Investor {
    /**
     * 接收股票价格变化通知
     * @param stock 变化的股票
     * @param isRise 股票是否上涨
     */
    void update(ConcreteStock stock, boolean isRise);}
4.ConcreteInvestor.java
public class ConcreteInvestor implements Investor {
    // 股民姓名
    private final String name;

    /**
     * 构造方法:初始化股民姓名
     * @param name 股民姓名
     */
    public ConcreteInvestor(String name) {
        this.name = name;
    }

    @Override
    public void update(ConcreteStock stock, boolean isRise) {
        double changeRate = stock.getPriceChangeRate();
        System.out.println("===== " + stock.getStockName() + "价格变化通知 =====");
        System.out.println("股民" + name + "收到消息:" + 
                (isRise ? "上涨" + String.format("%.2f", changeRate) + "%" : 
                          "下跌" + String.format("%.2f", Math.abs(changeRate)) + "%"));
        // 根据涨跌执行动作
        if (isRise) {
            System.out.println("股民" + name + ":赶紧加仓买!");
        } else {
            System.out.println("股民" + name + ":呜呜呜,亏麻了!");
        }
        System.out.println("当前股价:" + stock.getPrice() + "元\n");
    }}
4.StockObserverTest.java
public class StockObserverTest {
    public static void main(String[] args) {
        // 1. 创建具体股票(茅台,初始价格1800元)
        ConcreteStock maotaiStock = new ConcreteStock("贵州茅台", 1800.0);

        // 2. 创建股民(观察者)
        Investor zhangsan = new ConcreteInvestor("张三");
        Investor lisi = new ConcreteInvestor("李四");
        Investor wangwu = new ConcreteInvestor("王五");

        // 3. 股民订阅股票
        maotaiStock.registerObserver(zhangsan);
        maotaiStock.registerObserver(lisi);
        maotaiStock.registerObserver(wangwu);

        // 4. 模拟股票价格变化(上涨5%:1800→1890)
        System.out.println("【场景1:股票上涨5%】");
        maotaiStock.setPrice(1890.0);

        // 5. 模拟股票价格下跌6%(1890→1776.6)
        System.out.println("【场景2:股票下跌6%】");
        maotaiStock.setPrice(1776.6);

        // 6. 移除李四后,股票再上涨(1776.6→1865.43,上涨5%)
        System.out.println("【场景3:移除李四后,股票再上涨5%】");
        maotaiStock.removeObserver(lisi);
        maotaiStock.setPrice(1865.43);
    }}

实验 22:状态模式
本次实验属于模仿型实验,通过本次实验学生将掌握以下内容:
1、理解状态模式的动机,掌握该模式的结构;
2、能够利用状态模式解决实际问题。

[实验任务一]:银行账户
用Java代码模拟实现课堂上的“银行账户”的实例,要求编写客户端测试代码模拟用户存款和取款,注意账户对象状态和行为的变化。
实验要求:
1.画出对应的类图;
2.提交源代码;
3. 注意编程规范。
image

1. AccountState.java
public abstract class AccountState {
    protected Account account;

    /**
     * 构造方法:关联账户上下文
     * @param account 所属账户
     */
    public AccountState(Account account) {
        this.account = account;
    }

    /**
     * 存款行为(子类实现)
     * @param amount 存款金额
     */
    public abstract void deposit(double amount);

    /**
     * 取款行为(子类实现)
     * @param amount 取款金额
     */
    public abstract void withdraw(double amount);}
2. NormalState.java
public class NormalState extends AccountState {
    /**
     * 构造方法:初始化正常状态
     * @param account 所属账户
     */
    public NormalState(Account account) {
        super(account);
    }

    /**
     * 构造方法:带初始余额的正常状态
     * @param account 所属账户
     * @param balance 初始余额
     */
    public NormalState(Account account, double balance) {
        super(account);
        this.account.setBalance(balance);
    }

    @Override
    public void deposit(double amount) {
        // 正常状态存款:直接增加余额
        double newBalance = account.getBalance() + amount;
        account.setBalance(newBalance);
        System.out.println("账户【" + account.getAccountName() + "】存款" + amount + "元,当前余额:" + newBalance + "元(状态:正常)");
    }

    @Override
    public void withdraw(double amount) {
        double currentBalance = account.getBalance();
        if (amount <= currentBalance) {
            // 取款≤余额:正常取款
            double newBalance = currentBalance - amount;
            account.setBalance(newBalance);
            System.out.println("账户【" + account.getAccountName() + "】取款" + amount + "元,当前余额:" + newBalance + "元(状态:正常)");
        } else {
            // 取款>余额:切换为透支状态
            double newBalance = currentBalance - amount;
            account.setBalance(newBalance);
            account.setState(new OverdraftState(account, newBalance));
            System.out.println("账户【" + account.getAccountName() + "】取款" + amount + "元,当前余额:" + newBalance + "元(状态:透支)");
        }
    }}
3. OverdraftState.java
public class OverdraftState extends AccountState {
    // 透支额度(固定为1000元)
    private static final double OVERDRAFT_LIMIT = 1000;

    /**
     * 构造方法:初始化透支状态
     * @param account 所属账户
     */
    public OverdraftState(Account account) {
        super(account);
    }

    /**
     * 构造方法:带初始余额的透支状态
     * @param account 所属账户
     * @param balance 初始余额
     */
    public OverdraftState(Account account, double balance) {
        super(account);
        this.account.setBalance(balance);
    }

    @Override
    public void deposit(double amount) {
        double currentBalance = account.getBalance();
        double newBalance = currentBalance + amount;
        account.setBalance(newBalance);
        if (newBalance >= 0) {
            // 存款后余额≥0:切换为正常状态
            account.setState(new NormalState(account, newBalance));
            System.out.println("账户【" + account.getAccountName() + "】存款" + amount + "元,当前余额:" + newBalance + "元(状态:恢复正常)");
        } else {
            // 存款后仍透支:保持透支状态
            System.out.println("账户【" + account.getAccountName() + "】存款" + amount + "元,当前余额:" + newBalance + "元(状态:透支)");
        }
    }

    @Override
    public void withdraw(double amount) {
        double currentBalance = account.getBalance();
        double overdraftAmount = Math.abs(currentBalance); // 当前透支金额
        if (amount <= (OVERDRAFT_LIMIT - overdraftAmount)) {
            // 取款≤剩余透支额度:允许透支取款
            double newBalance = currentBalance - amount;
            account.setBalance(newBalance);
            System.out.println("账户【" + account.getAccountName() + "】透支取款" + amount + "元,当前余额:" + newBalance + "元(状态:透支)");
        } else {
            // 取款>剩余透支额度:拒绝取款
            System.out.println("账户【" + account.getAccountName() + "】取款失败!剩余透支额度:" + (OVERDRAFT_LIMIT - overdraftAmount) + "元,请求取款:" + amount + "元");
        }
    }}
4. Account.java
public class Account {
    // 账户姓名
    private final String accountName;
    // 账户余额
    private double balance;
    // 当前账户状态
    private AccountState state;

    /**
     * 构造方法:初始化账户
     * @param accountName 账户姓名
     * @param initBalance 初始余额
     */
    public Account(String accountName, double initBalance) {
        this.accountName = accountName;
        this.balance = initBalance;
        // 初始状态:余额≥0为正常状态,否则为透支状态
        if (initBalance >= 0) {
            this.state = new NormalState(this, initBalance);
        } else {
            this.state = new OverdraftState(this, initBalance);
        }
        System.out.println("账户【" + accountName + "】创建成功,初始余额:" + initBalance + "元(初始状态:" + (initBalance >= 0 ? "正常" : "透支") + ")");
    }

    /**
     * 存款操作(委托给当前状态)
     * @param amount 存款金额
     */
    public void deposit(double amount) {
        if (amount <= 0) {
            System.out.println("存款失败!金额必须大于0");
            return;
        }
        state.deposit(amount);
    }

    /**
     * 取款操作(委托给当前状态)
     * @param amount 取款金额
     */
    public void withdraw(double amount) {
        if (amount <= 0) {
            System.out.println("取款失败!金额必须大于0");
            return;
        }
        state.withdraw(amount);
    }

    // Getter & Setter
    public void setState(AccountState state) {
        this.state = state;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    public String getAccountName() {
        return accountName;
    }}
5. AccountStateTest.java
public class AccountStateTest {
    public static void main(String[] args) {
        // 1. 创建账户(初始余额2000元,正常状态)
        Account account = new Account("张三", 2000);

        // 2. 正常状态取款1500元
        System.out.println("\n--- 操作1:正常取款1500元 ---");
        account.withdraw(1500);

        // 3. 正常状态取款1000元(导致透支)
        System.out.println("\n--- 操作2:正常取款1000元(透支) ---");
        account.withdraw(1000);

        // 4. 透支状态存款800元(仍透支)
        System.out.println("\n--- 操作3:透支存款800元 ---");
        account.deposit(800);

        // 5. 透支状态取款200元(在透支额度内)
        System.out.println("\n--- 操作4:透支取款200元 ---");
        account.withdraw(200);

        // 6. 透支状态存款1500元(恢复正常)
        System.out.println("\n--- 操作5:透支存款1500元(恢复正常) ---");
        account.deposit(1500);

        // 7. 正常状态取款3000元(再次透支)
        System.out.println("\n--- 操作6:正常取款3000元(透支) ---");
        account.withdraw(3000);

        // 8. 透支状态取款1200元(超过透支额度)
        System.out.println("\n--- 操作7:透支取款1200元(超限) ---");
        account.withdraw(1200);
    }}

实验 23:策略模式
本次实验属于模仿型实验,通过本次实验学生将掌握以下内容:
1、理解策略模式的动机,掌握该模式的结构;
2、能够利用策略模式解决实际问题。

[实验任务一]:旅行方式的选择
旅游的出行方式有乘坐飞机旅行、乘火车旅行和自行车游,不同的旅游方式有不同的实现过程,客户可以根据自己的需要选择一种合适的旅行方式。
实验要求:
1.画出对应的类图;
2.提交源代码;
3.注意编程规范。
image

1. TravelStrategy.java
public interface TravelStrategy {
    /**
     * 执行具体的旅行方式
     */
    void travel();}
2. PlaneTravel.java
public class PlaneTravel implements TravelStrategy {
    @Override
    public void travel() {
        System.out.println("选择飞机旅行:");
        System.out.println("1. 预订机票(需身份证/护照)");
        System.out.println("2. 提前2小时到机场办理值机");
        System.out.println("3. 安检后登机,飞行时间短,适合长途旅行");
        System.out.println("------------------------");
    }}
3. TrainTravel.java
public class TrainTravel implements TravelStrategy {
    @Override
    public void travel() {
        System.out.println("选择火车旅行:");
        System.out.println("1. 购买火车票(需身份证)");
        System.out.println("2. 提前30分钟到车站检票");
        System.out.println("3. 乘坐高铁/普速列车,站点覆盖广,性价比高");
        System.out.println("------------------------");
    }}
4. BikeTravel.java
public class BikeTravel implements TravelStrategy {
    @Override
    public void travel() {
        System.out.println("选择自行车旅行:");
        System.out.println("1. 检查自行车车况(轮胎、刹车等)");
        System.out.println("2. 准备骑行装备(头盔、水壶)");
        System.out.println("3. 沿途欣赏风景,适合短途休闲旅行");
        System.out.println("------------------------");
    }}
5. TravelContext.java
public class TravelContext {
    // 当前使用的旅行策略
    private TravelStrategy strategy;

    /**
     * 构造方法:初始化策略
     * @param strategy 旅行策略
     */
    public TravelContext(TravelStrategy strategy) {
        this.strategy = strategy;
    }

    /**
     * 切换旅行策略
     * @param strategy 新的旅行策略
     */
    public void setStrategy(TravelStrategy strategy) {
        this.strategy = strategy;
    }

    /**
     * 执行当前策略的旅行行为
     */
    public void executeTravel() {
        strategy.travel();
    }}
6. TravelStrategyTest.java
public class TravelStrategyTest {
    public static void main(String[] args) {
        // 1. 选择飞机旅行
        System.out.println("===== 第一次旅行选择 =====");
        TravelContext context = new TravelContext(new PlaneTravel());
        context.executeTravel();

        // 2. 切换为火车旅行
        System.out.println("===== 第二次旅行选择 =====");
        context.setStrategy(new TrainTravel());
        context.executeTravel();

        // 3. 切换为自行  车旅行
        System.out.println("===== 第三次旅行选择 =====");
        context.setStrategy(new BikeTravel());
        context.executeTravel();
    }}
posted @ 2026-01-03 13:34  liu某人  阅读(2)  评论(0)    收藏  举报