第1关:状态模式
任务描述
请你为某商城设计一个会员程序,要求如下:
-
商城将顾客分为普通、黄金、VIP 三个等级,普通会员消费时没有折扣,黄金会员95折,VIP 会员85折;
-
积分规则:按单笔消费金额等额取整法,例如客户消费1元积1分,消费1.5元也是积一分,消费2元则积2分;
-
刚加入的顾客被归入普通会员,要求填入姓名;当顾客积分大于等于500时自动升级为黄金会员,下次享受黄金会员待遇;当积分大于等于2000时自动升级为 VIP 会员,下次起享受 VIP 会员待遇。注意:会员升级过程不能跳级。
实现方式
-
确定哪些类是上下文。 它可能是包含依赖于状态的代码的已有类; 如果特定于状态的代码分散在多个类中, 那么它可能是一个新的类;
-
声明状态接口。 虽然你可能会需要完全复制上下文中声明的所有方法, 但最好是仅把关注点放在那些可能包含特定于状态的行为的方法上;
-
为每个实际状态创建一个继承于状态接口的类。 然后检查上下文中的方法并将与特定状态相关的所有代码抽取到新建的类中。在将代码移动到状态类的过程中, 你可能会发现它依赖于上下文中的一些私有成员。 你可以采用以下几种变通方式:
-
将这些成员变量或方法设为公有;
-
将需要抽取的上下文行为更改为上下文中的公有方法, 然后在状态类中调用。 这种方式简陋却便捷, 你可以稍后再对其进行修补;
-
将状态类嵌套在上下文类中。 这种方式需要你所使用的编程语言支持嵌套类。
-
-
在上下文类中添加一个状态接口类型的引用成员变量, 以及一个用于修改该成员变量值的公有设置器;
-
再次检查上下文中的方法, 将空的条件语句替换为相应的状态对象方法;
-
为切换上下文状态, 你需要创建某个状态类实例并将其传递给上下文。 你可以在上下文、 各种状态或客户端中完成这项工作。 无论在何处完成这项工作, 该类都将依赖于其所实例化的具体类。
编程要求
根据提示,在右侧编辑器 Begin-End 内补全代码,需要补充代码的文件如下:
- AbstractState.java
- CommonState.java
- GoldState.java
- clubAccount.java
测试说明
输入第一行表示顾客姓名,第二行给出一个正整数 n(n⩽10)表示消费次数。随后 n 行,每行给出1个实数(消费金额)。输出 n 行结果,格式为 XX 本次消费金额为 XX,折扣后为 XX
测试输入: 张三 3 612.0 1621.0 100.0
预期输出: 张三注册成功 普通会员本次消费金额:612.0,折扣后:612.0,当前积分:612 黄金会员本次消费金额:1621.0,折扣后:1539.9,当前积分:2151 VIP会员本次消费金额:100.0,折扣后:85.0,当前积分:2236

1
java
package step1;
public abstract class AbstractState {
protected clubAccount account;//账户
protected double discount;//折扣比例
protected int userPoints;//积分
protected String stateName;//状态名
public void Consume(double money) {
/********** Begin *********/
// 先计算折扣后金额和积分
double discountedAmount = money * discount;
int pointsEarned = (int) Math.floor(discountedAmount);
userPoints += pointsEarned;
// 检查是否需要状态变更
checkState();
/********** End *********/
///现金消费
System.out.println(stateName+"本次消费金额:"+money+",折扣后:"+String.format("%.1f",discountedAmount)+",当前积分:"+userPoints);
}
///若有积分抵现金或领取礼物则需要修改checkState原型,请自由扩展积分消费函数
public abstract void checkState();
}
2

java
package step1;
public class CommonState extends AbstractState{
///若有降级情况,则需要用到CommonState(AbstractState state)
public CommonState(AbstractState state) {
this.userPoints = state.userPoints;
this.stateName = "普通会员";
this.account = state.account;
this.discount = 1;
}
public CommonState(clubAccount account) {
this.account = account;
this.userPoints = 0;
this.stateName = "普通会员";
this.discount = 1;
}
@Override
public void checkState() {
/********** Begin *********/
if (userPoints >= 500) {
// 升级为黄金会员
account.setState(new GoldState(this));
// 状态变更后需要重新检查状态(因为可能直接满足VIP条件)
account.getState().checkState();
}
/********** End *********/
}
}
3

java
package step1;
public class GoldState extends AbstractState{
public GoldState(AbstractState state) {
this.userPoints = state.userPoints;
this.stateName = "黄金会员";
this.account = state.account;
this.discount = 0.95;
}
@Override
public void checkState() {
/********** Begin *********/
if (userPoints >= 2000) {
// 升级为VIP会员
account.setState(new VIPState(this));
} else if (userPoints < 500) {
// 降级为普通会员
account.setState(new CommonState(this));
}
/********** End *********/
}
}
4

java
package step1;
public class clubAccount {
private String name;//姓名
private AbstractState state;//当前状态
public clubAccount(String name) {
this.name = name;
this.state = new CommonState(this);
System.out.println(this.name + "注册成功!");
}
public void setState(AbstractState state) {
this.state = state;
}
public AbstractState getState() {
return this.state;
}
public void Consume(double money) {
/********** Begin *********/
state.Consume(money);
/********** End *********/
}
}
