一、功能简介
1.功能
此程序实现了简单的手机套餐办理,包括注册新用户,购取手机卡,选择手机套餐,并模拟了用户充值话费以及使用手机套餐来进行消费,包括通话、短信、流量计费等功能。
2.套餐详情
3.套餐计费方式
二、设计类和接口
1.设计接口功能
接口功能中包括发送消息功能、通话功能、使用流量功能。
2.设计套餐类
a.设计父类套餐类
包括了流量、短信数量、通话时长和套餐名字等属性。
b.设计子类套餐类
包括TalkPackage类、NetPackage类、SuperPackage类,并继承接口。
3.设计手机卡类
属性包括手机号码、手机使用的套餐方案、话费余额以及手机已使用的流量、通话时长、短信数量。
4.设计用户类
创建User用户类,并添加用户姓名、用户Id、以及用户所使用的的手机卡等属性。
5.测试类
依次实例化对象,并传入参数调用方法,实现手机套餐办理和使用。
三、具体实现
1.接口实现
package work7_2022_09_25;
/*
* 套餐的基本功能接口
* card:使用功能的手机号
* */
public interface PackageFunction {
//上网方法
//flow:上网流量,单位M
void net(int flow,MobileCard card);
//发送短信
//count:短信条数
void sent(int count,MobileCard card);
//通话方法
//time:通话时长
void call(int time,MobileCard card);
}
2.父类套餐类实现(抽象类)
package work7_2022_09_25;
/*
* 套餐的的基本属性
* */
public abstract class Package {
private String packageName;//套餐名称
private int talkTime;//通话时长
private int smsCount;//短信条数
private double flow;//上网流量,单位G
private int price;//资费
public Package(String packageName, int talkTime, int smsCount, double flow, int price) {
super();
this.packageName = packageName;
this.talkTime = talkTime;
this.smsCount = smsCount;
this.flow = flow;
this.price = price;
}
public abstract void show();
public String getPackageName() {
return packageName;
}
public void setPackageName(String packageName) {
this.packageName = packageName;
}
public int getTalkTime() {
return talkTime;
}
public void setTalkTime(int talkTime) {
this.talkTime = talkTime;
}
public int getSmsCount() {
return smsCount;
}
public void setSmsCount(int smsCount) {
this.smsCount = smsCount;
}
public double getFlow() {
return flow;
}
public void setFlow(double flow) {
this.flow = flow;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
3.子类实现
a.话痨套餐类
package work7_2022_09_25;
import java.text.DecimalFormat;
/*
* 话痨套餐
* */
public class TalkPackage extends Package implements PackageFunction {
private final static double FLOWRULE = 2;//流量计费规则
private final static double SMSCOUNTRULE = 0.1;//短信计费规则
private final static double TIMERULE = 0.2;//通话计费规则
private final static double FLOW = 0;//套餐流量(GB)
private final static int SMSCOUNT = 30;//套餐短信
private final static int TALKETIME = 500;//套餐通话时长(分钟)
private final static int PRICE = 58;//套餐的资费
private final static String PACKAGENAME = "话痨套餐";//套餐名字
public TalkPackage() {
//初始化套餐基本信息
super(PACKAGENAME,TALKETIME,SMSCOUNT,FLOW, PRICE);
}
@Override
public void net(int flow, MobileCard card) {
//如果之前已用流量大于套餐流量
//直接实行扣费方案
if(card.getFlow() >= super.getFlow()) {
double money = flow * FLOWRULE;//超出套餐流量部分所需扣除的费用
card.feeDeduction(money);
card.incrTraffic(flow);
}else {//之前已使用流量未超出套餐流量,先将当前使用流量加入到之前使用的流量中,计算已使用流量总数
card.incrTraffic(flow);
if(card.getFlow() > super.getFlow()) {//如果当前已使用流量大于套餐流量则计算超出部分需要支付的话费
System.out.println("当前套餐流量余量已经用完,继续使用则会扣除额外费用");
int newFlow = (int)((card.getFlow() - super.getFlow()) * 1024);
double money = flow * FLOWRULE;//超出套餐流量部分所需扣除的费用
card.feeDeduction(money);
}
}
}
@Override
public void sent(int count, MobileCard card) {
//如果之前已用短信条数大于套餐条数
//直接实行扣费方案
if(card.getSmsCount() >= super.getSmsCount()) {
double money = count * SMSCOUNTRULE;//超出套餐短信部分所需扣除的费用
card.feeDeduction(money);
card.incrMessages(count);
}else {//之前已使用短信数量未超出套餐短信数量,先将当前使用短信数量加入到之前已使用的短信数量中,计算已发送短信总数
card.incrMessages(count);
if(card.getSmsCount() > super.getSmsCount()) {//如果当前已使用流量大于套餐短信数量则计算超出部分需要支付的话费
System.out.println("当前套餐短信余量已经用完,继续使用则会扣除额外费用");
int newcount = card.getSmsCount() - super.getSmsCount();
double money = newcount * SMSCOUNTRULE;//超出套餐短信部分所需扣除的费用
card.feeDeduction(money);
}
}
}
@Override
public void call(int time, MobileCard card) {
if(card.getTalkTime() >= super.getTalkTime()) {
double money = time * TIMERULE;//超出套餐通话时长部分所需扣除的费用
card.feeDeduction(money);
card.incrCall(time);
}else {//之前已使用短信数量未超出套通话时长,先将当前使用通话时长加入到之前已使用的通话时长中,计算已通话时长总数
card.incrCall(time);
if(card.getTalkTime() > super.getTalkTime()) {//如果当前已使用通话时长大于套餐通话时长则计算超出部分需要支付的话费
System.out.println("当前套餐通话余量已经用完,继续使用则会扣除额外费用");
int newtime = card.getTalkTime() - super.getTalkTime();
double money = newtime * TIMERULE;//超出套餐通话时长部分所需扣除的费用
card.feeDeduction(money);
}
}
}
//当前套餐信息
@Override
public void show() {
DecimalFormat df = new DecimalFormat("0.00");
System.out.println("当前套餐信息如下:");
System.out.println("*********************************************");
System.out.println("套餐名字:\t" + PACKAGENAME +
"\n套餐资费:\t" + PRICE + "元" +
"\n总流量:\t\t" + df.format(FLOW) +"GB" +
"\n通话总时长:\t" + TALKETIME + "分钟" +
"\n短信总数:\t" + SMSCOUNT + "条");
System.out.println("*********************************************");
}
}
b.网虫套餐类
package work7_2022_09_25;
/*
* 网虫套餐
* */
public class NetPackage extends Package implements PackageFunction {
private final static double FLOWRULE = 0.5;//流量计费规则
private final static double SMSCOUNTRULE = 0.2;//短信计费规则
private final static double TIMERULE = 0.5;//通话计费规则
private final static double FLOW = 10;//套餐流量(GB)
private final static int SMSCOUNT = 0;//套餐短信
private final static int TALKETIME = 0;//套餐通话时长(分钟)
private final static int PRICE = 68;//套餐的资费
private final static String PACKAGENAME = "网虫套餐";//套餐名字
public NetPackage() {
//初始化套餐基本信息
super(PACKAGENAME,TALKETIME,SMSCOUNT,FLOW, PRICE);
}
@Override
public void net(int flow, MobileCard card) {
//如果之前已用流量大于套餐流量
//直接实行扣费方案
if(card.getFlow() >= super.getFlow()) {
double money = flow * FLOWRULE;//超出套餐流量部分所需扣除的费用
card.feeDeduction(money);
card.incrTraffic(flow);
}else {//之前已使用流量未超出套餐流量,先将当前使用流量加入到之前使用的流量中,计算已使用流量总数
card.incrTraffic(flow);
if(card.getFlow() > super.getFlow()) {//如果当前已使用流量大于套餐流量则计算超出部分需要支付的话费
System.out.println("当前套餐流量余量已经用完,继续使用则会扣除额外费用");
int newFlow = (int)((card.getFlow() - super.getFlow()) * 1024);
double money = flow * FLOWRULE;//超出套餐流量部分所需扣除的费用
card.feeDeduction(money);
}
}
}
@Override
public void sent(int count, MobileCard card) {
//如果之前已用短信条数大于套餐条数
//直接实行扣费方案
if(card.getSmsCount() >= super.getSmsCount()) {
double money = count * SMSCOUNTRULE;//超出套餐短信部分所需扣除的费用
card.feeDeduction(money);
card.incrMessages(count);
}else {//之前已使用短信数量未超出套餐短信数量,先将当前使用短信数量加入到之前已使用的短信数量中,计算已发送短信总数
card.incrMessages(count);
if(card.getSmsCount() > super.getSmsCount()) {//如果当前已使用流量大于套餐短信数量则计算超出部分需要支付的话费
System.out.println("当前套餐短信余量已经用完,继续使用则会扣除额外费用");
int newcount = card.getSmsCount() - super.getSmsCount();
double money = newcount * SMSCOUNTRULE;//超出套餐短信部分所需扣除的费用
card.feeDeduction(money);
}
}
}
@Override
public void call(int time, MobileCard card) {
if(card.getTalkTime() >= super.getTalkTime()) {
double money = time * TIMERULE;//超出套餐通话时长部分所需扣除的费用
card.feeDeduction(money);
card.incrCall(time);
}else {//之前已使用短信数量未超出套通话时长,先将当前使用通话时长加入到之前已使用的通话时长中,计算已通话时长总数
card.incrCall(time);
if(card.getTalkTime() > super.getTalkTime()) {//如果当前已使用通话时长大于套餐通话时长则计算超出部分需要支付的话费
System.out.println("当前套餐通话余量已经用完,继续使用则会扣除额外费用");
int newtime = card.getTalkTime() - super.getTalkTime();
double money = newtime * TIMERULE;//超出套餐通话时长部分所需扣除的费用
card.feeDeduction(money);
}
}
}
//当前套餐信息
@Override
public void show() {
DecimalFormat df = new DecimalFormat("0.00");
System.out.println("当前套餐信息如下:");
System.out.println("*********************************************");
System.out.println("套餐名字:\t" + PACKAGENAME +
"\n套餐资费:\t" + PRICE + "元" +
"\n总流量:\t\t" + df.format(FLOW) +"GB" +
"\n通话总时长:\t" + TALKETIME + "分钟" +
"\n短信总数:\t" + SMSCOUNT + "条");
System.out.println("*********************************************");
}
}
c.超人套餐类
package work7_2022_09_25;
/*
* 超人套餐
* */
public class SuperPackage extends Package implements PackageFunction {
private final static double FLOWRULE = 1;//流量计费规则
private final static double SMSCOUNTRULE = 0.1;//短信计费规则
private final static double TIMERULE = 0.2;//通话计费规则
private final static double FLOW = 5;//套餐流量(GB)
private final static int SMSCOUNT = 50;//套餐短信
private final static int TALKETIME = 200;//套餐通话时长(分钟)
private final static int PRICE = 78;//套餐的资费
private final static String PACKAGENAME = "超人套餐";//套餐名字
public SuperPackage() {
//初始化套餐基本信息
super(PACKAGENAME,TALKETIME,SMSCOUNT,FLOW, PRICE);
}
@Override
public void net(int flow, MobileCard card) {
//如果之前已用流量大于套餐流量
//直接实行扣费方案
if(card.getFlow() >= super.getFlow()) {
double money = flow * FLOWRULE;//超出套餐流量部分所需扣除的费用
card.feeDeduction(money);
card.incrTraffic(flow);
}else {//之前已使用流量未超出套餐流量,先将当前使用流量加入到之前使用的流量中,计算已使用流量总数
card.incrTraffic(flow);
if(card.getFlow() > super.getFlow()) {//如果当前已使用流量大于套餐流量则计算超出部分需要支付的话费
System.out.println("当前套餐流量余量已经用完,继续使用则会扣除额外费用");
int newFlow = (int)((card.getFlow() - super.getFlow()) * 1024);
double money = flow * FLOWRULE;//超出套餐流量部分所需扣除的费用
card.feeDeduction(money);
}
}
}
@Override
public void sent(int count, MobileCard card) {
//如果之前已用短信条数大于套餐条数
//直接实行扣费方案
if(card.getSmsCount() >= super.getSmsCount()) {
double money = count * SMSCOUNTRULE;//超出套餐短信部分所需扣除的费用
card.feeDeduction(money);
card.incrMessages(count);
}else {//之前已使用短信数量未超出套餐短信数量,先将当前使用短信数量加入到之前已使用的短信数量中,计算已发送短信总数
card.incrMessages(count);
if(card.getSmsCount() > super.getSmsCount()) {//如果当前已使用流量大于套餐短信数量则计算超出部分需要支付的话费
System.out.println("当前套餐短信余量已经用完,继续使用则会扣除额外费用");
int newcount = card.getSmsCount() - super.getSmsCount();
double money = newcount * SMSCOUNTRULE;//超出套餐短信部分所需扣除的费用
card.feeDeduction(money);
}
}
}
@Override
public void call(int time, MobileCard card) {
if(card.getTalkTime() >= super.getTalkTime()) {
double money = time * TIMERULE;//超出套餐通话时长部分所需扣除的费用
card.feeDeduction(money);
card.incrCall(time);
}else {//之前已使用短信数量未超出套通话时长,先将当前使用通话时长加入到之前已使用的通话时长中,计算已通话时长总数
card.incrCall(time);
if(card.getTalkTime() > super.getTalkTime()) {//如果当前已使用通话时长大于套餐通话时长则计算超出部分需要支付的话费
System.out.println("当前套餐通话余量已经用完,继续使用则会扣除额外费用");
int newtime = card.getTalkTime() - super.getTalkTime();
double money = newtime * TIMERULE;//超出套餐通话时长部分所需扣除的费用
card.feeDeduction(money);
}
}
}
//当前套餐信息
@Override
public void show() {
DecimalFormat df = new DecimalFormat("0.00");
System.out.println("当前套餐信息如下:");
System.out.println("*********************************************");
System.out.println("套餐名字:\t" + PACKAGENAME +
"\n套餐资费:\t" + PRICE + "元" +
"\n总流量:\t\t" + df.format(FLOW) +"GB" +
"\n通话总时长:\t" + TALKETIME + "分钟" +
"\n短信总数:\t" + SMSCOUNT + "条");
System.out.println("*********************************************");
}
}
4.电话卡类实现
package work7_2022_09_25;
import java.text.DecimalFormat;
public class MobileCard {
private String cardId;//手机号
private double balance;//账户余额
private Package _paPackage;//套餐
private double flow;//当月已用流量
private int smsCount;//当月已用短信条数
private int talkTime;//当月已用通话时长
public double getFlow() {
return flow;
}
public void setFlow(double flow) {
this.flow = flow;
}
public int getSmsCount() {
return smsCount;
}
public void setSmsCount(int smsCount) {
this.smsCount = smsCount;
}
public int getTalkTime() {
return talkTime;
}
public void setTalkTime(int talkTime) {
this.talkTime = talkTime;
}
public String getCardId() {
return cardId;
}
public void setCardId(String cardId) {
this.cardId = cardId;
}
public double getBalance() {
return balance;
}
public void setBalance(double blance) {
this.balance = blance;
}
public Package get_paPackage() {
return _paPackage;
}
public void set_paPackage(Package _paPackage) {
this._paPackage = _paPackage;
}
//有参构造函数,主要传入值为电话号码、充值金额、办理套餐
public MobileCard(String cardId, double blance, Package _paPackage) {
//充值话费
this.balance = blance;
//指定手机号码
this.cardId = cardId;
if(this.balance >= _paPackage.getPrice()) {
//添加套餐
this._paPackage = _paPackage;
//扣除套餐费用
this.balance -= _paPackage.getPrice();
//初始化短信、流量、通话时长使用情况
this.smsCount = 0;
this.flow = 0;
this.talkTime = 0;
}else {
System.out.println("余额不足!请充值后继续办理!");
System.out.println(this.balance + "--" + _paPackage.getPrice());
}
}
//充值方法
public void recharge(double money) {
this.balance += money;
}
//扣费方法
public void feeDeduction(double money) {
if(money > this.balance) {
System.out.println("余额不足,请充值!");
}else {
this.balance -= money;
}
}
//增加使用流量
public void incrTraffic(double flow) {
this.flow += (flow / 1024);
}
//增加通话时长
public void incrCall(int time) {
this.talkTime += time;
}
//增加短信条数
public void incrMessages(int count) {
this.smsCount += count;
}
@Override
public String toString() {
return "MobileCardcard [cardId=" + cardId + ", blance=" + balance + ", _paPackage=" + _paPackage + "]";
}
public void show() {
DecimalFormat df = new DecimalFormat("0.00");
this.balance = Double.parseDouble(df.format(this.balance));
this.flow = Double.parseDouble(df.format(this.flow));
System.out.println("当前账户信息如下:");
System.out.println("***************************************************************");
System.out.println("手机号:\t" + this.cardId +
"\n话费余额:\t" + this.balance + "元" +
"\n已用流量:\t" + this.flow + "G" +
"\n流量余量:\t" + (this.flow > this.get_paPackage().getFlow() ? 0 : this.get_paPackage().getFlow() - this.flow) + "G" +
"\n已用短信:\t" + this.smsCount +"条" +
"\n可用短信:\t" + (this.smsCount > this.get_paPackage().getSmsCount() ? 0 : this.get_paPackage().getSmsCount() - this.smsCount) + "条" +
"\n已用通话时长:\t" + this.talkTime + "分钟" +
"\n可用通话时长:\t" + (this.talkTime > this.get_paPackage().getTalkTime() ? 0 : this.get_paPackage().getTalkTime() - this.talkTime) + "分钟");
System.out.println("***************************************************************");
}
}
5.用户类实现
package work7_2022_09_25;
public class User {
private String UserId;//用户Id
private String Username;//用户姓名
private MobileCard card;
public String getUserId() {
return UserId;
}
public void setUserId(String userId) {
UserId = userId;
}
public String getUsername() {
return Username;
}
public void setUsername(String username) {
Username = username;
}
public MobileCard getCard() {
return card;
}
public void setCard(MobileCard card) {
this.card = card;
}
public User(String userId, String username, MobileCard card) {
super();
UserId = userId;
Username = username;
this.card = card;
}
@Override
public String toString() {
return "User [UserId=" + UserId + ", Username=" + Username + ", card=" + card + "]";
}
}
6.测试类实现
package work7_2022_09_25;
public class Test {
public static void main(String[] args) {
//创建一个话痨类套餐对象
TalkPackage talkPackage = new TalkPackage();
//创建一张电话卡,并初始化
//号码为13617452397
//初始金额为200
//初始套餐为话痨套餐
MobileCard card = new MobileCard("13617452397",200,talkPackage);
//创建一个名为张三用户,其用户Id作为唯一标识符
//为张三购买一张电话卡
User user = new User("11abc","张三",card);
System.out.println("消费前:");
user.getCard().show();
//模仿张三上网花了50M,通话花了十分钟,发了四十条短信
PackageFunction test = new TalkPackage();
test.net(50, card);
test.sent(40, card);
System.out.println("消费后:");
test.call(10, card);
user.getCard().show();
System.out.println("继续消费后:");
test.call(500, card);
user.getCard().show();
//充值
System.out.println("充值后:");
user.getCard().recharge(50);
user.getCard().show();
//消费过量
System.out.println("消费过量后:");
test.call(10000, card);
user.getCard().show();
}
}
四、测试
1.套餐使用测试
2.套餐查询测试
测试类中的调用方法
//查看当前用户套餐详情
user.getCard().get_paPackage().show();
结果如下:
五、总结
类之间继承的原因总是为了抽象出公共的部分,增加代码的利用率。通过上述实验我们知道,父类对象作为方法参数可以实现多态。关注的是一类事物。用接口作为方法参数的多态,关注的是一种能力或者是一种行为,显然适用范围更加广泛。
比如,一个方法需要实现开车(drive)的能力,可以用传入一个司机(Driver)的对象。但是往往不只有司机才能开车,开车的还可以是学生、老师、医生等其他人员。假设此时有一个接口(Drivable),接口里面有开车的行为,那么只要实现这个接口就有开车的能力。
从实验和上述举例我们可以发现,接口大大地增强了程序的拓展性。某个方法需要什么功能,我们就定义一个接口,实现这个接口的类可以不断地更新,调用这个接口的代码就不会随着实现类的改变需要调整,这样就降低了模块直接的耦合性。这也就是为什么,项目里面每个模块都是调用其他模块的接口,而不是直接调用其实现类。