题目集8-9总结性Blog

一、前言

这一次大作业的难度相较于上一次电梯问题来说难度上可以说是降低了很多,和电梯难度在于电梯运行逻辑的构建不同,这一次大作业的逻辑很简单,存储信息输出信息,而难点便是这一次大作业的着重点在于面向对象设计原则中的单一职责原则、里氏代换原则、开闭原则以及合成复用原则。你不但需要设计好所有类,而且你还必须满足单一职责原则、里氏代换原则、开闭原则以及合成复用原则。但好在在大作业之前就有几个小题来给予你思路,比如题目集8的点线面问题重构中要求我们对先前的点线面问题进行重构,要求使用继承和多态,而继承和多态恰恰符合里氏代换原则当然也间接符合开闭原则,这也就提供了部分思路至少我们不会无从下手。

二、设计与分析

第一次题目的设计与分析

题目要求:本次题目模拟某客户到该航空公司办理一次货运业务的过程: 航空公司提供如下信息: 航班信息(航班号,航班起飞机场所在城市,航班降落机场所在城市,航班 日期,航班最大载重量) 客户填写货运订单并进行支付,需要提供如下信息:  客户信息(姓名,电话号码等)  货物信息(货物名称,货物包装长、宽、高尺寸,货物重量等)  运送信息(发件人姓名、电话、地址,收件人姓名、电话、地址,所选 航班号,订单日期)  支付方式(支付宝支付、微信支付)

 

按如下顺序分别输入客户信息、货物信息、航班信息以及订单信息。

 

客户编号
客户姓名
客户电话
客户地址
运送货物数量
[货物编号
货物名称
货物宽度
货物长度
货物高度
货物重量
]//[]内的内容输入次数取决于“运送货物数量”,输入不包含“[]”
航班号
航班起飞机场
航班降落机场
航班日期(格式为YYYY-MM-DD)
航班最大载重量
订单编号
订单日期(格式为YYYY-MM-DD)
发件人地址
发件人姓名
发件人电话
收件人地址
收件人姓名
收件人电话
按以下格式输出:

客户:姓名(电话)订单信息如下:
-----------------------------------------
航班号:
订单号:
订单日期:
发件人姓名:
发件人电话:
发件人地址:
收件人姓名:
收件人电话:
收件人地址:
订单总重量(kg):
微信支付金额:

货物明细如下:
-----------------------------------------
明细编号 货物名称 计费重量 计费费率 应交运费
1 ...
2 ...

 

 

设计类图:

类的设计:

Main类:主类

public static void main(String[] args)

Customer类:获得顾客类

private String name;
private String phone;
private String ID;
private String address;
public Customer(String name,String phone,String address,String id)public void setname(String name)
public void setphone(String phone) public void setaddress(String address) public void setID(String id) public String getID() public String getname() public String getphone() public String getaddress()

Cargo_objects类:抽象父类货物对象,获得运输货物的目标对象

public void setname(String name)
public void setphone(String phone)
public void setaddress(String address)
public String getname()
public String getphone()
public String getaddress()
public Cargo_objects(String name,String phone,String address) 

Consigner类:Cargo_objects类的子类,获得收件人

public Consigner(String name,String phone,String address)

Consignee类:Cargo_objects的子类,获得寄件人

public Consigner(String name,String phone,String address)

Flight类:航班类,获得航班信息

private String flightnum;
private String departureCity;
private String arrivalCity;
private int maxweight;
private LocalDate flightDate;
public Fight(String num, String departurecity, String arrivalcity, String date, int max)
public String getFlightnum() 
public void setFlightnum(String flightnum) 
public String getDepartureCity()
public void setDepartureCity(String departureCity) 
public String getArrivalCity() 
public void setArrivalCity(String arrivalCity)
public LocalDate getFlightDate() 
public void setFlightDate(LocalDate flightDate) 
public int getMaxweight() 
public void setMaxweight(int maxweight) 

Goods类:货物类,封装存储所有货物的信息

private List<Goods> goods;
private Goods goodsinfo;
private Customer customer;
private Consigner consigner;
private Consignee consignee;
private int goodsnum = 0;
private String orderid;
private String orderDate;
public Order(List<Goods> goods, Customer customer, Consigner consigner, Consignee consignee,String orderid,String orderDate,int goodsCount) 
public List<Goods> getGoods()
public String getid() 
public void setorderid(String id) 
public void setdate(String date)
public String getDate()
public void setGoods(List<Goods> goods)
public Goods getGoodsinfo()
public void setGoodsinfo(Goods goodsinfo) 
public Customer getCustomer()
public void setCustomer(Customer customer)
public Consigner getConsigner()
public void setConsigner(Consigner consigner)
public Consignee getConsignee() 
public void setConsignee(Consignee consignee)
public int getGoodsnum() 
public void setGoodsnum(int goodsnum)
public void addgoods(Goods goods) 

Payment类:抽象父类,获得支付方法

abstract String getpayingway()

vxpay类:Payment类子类,微信支付方式

public String getpayingway()

alipay类:Payment类子类,支付宝支付方式

public String getpayingway()

Rate类:费率策略工厂,获得费率

public static RateStrategy getStrategy(double chargeWeight)

RateStrategy类:Rate类相关接口,定义根据货物类型获得费率方法

double getRate(double weight)

Low20类:计费重量小于20kg的策略实现

 public double getRate(double weight)

Low50类:计费重量小于50kg的策略实现

 public double getRate(double weight)

Low100类:计费重量小于100Kg的策略实现

 public double getRate(double weight)

Up100类:计费重量大于100kg的策略实现

 public double getRate(double weight)

Calculatemoney类:计算运费

private  Calculateweight weightCalculator = new Calculateweight();
public double calculateGoodsCost(Goods goods)
public double calculateTotalCost(List<Goods> goodsList) 

Calculateweight类:计算计费重量

private double VolumeWeight;
public double calculateVolumeWeight(double lenght,double width,double height)
public double dedetermineChargeWeight(Goods goods) 

 

报表:

报表分析:

整体上看,代码行数596,规模中等。语句数239,语句密度适中。从代码结构和复杂度上看,分支语句占比1.3%,代码逻辑分支少,结构较线性,和之前的作业相比较下可以看出这次作业的逻辑确实简单。方法调用语句48,方法间协作较频繁,或许之后可以优化一下使协作减少一些。带注释行占比15.4%,注释量较少,可适当增加以提升可读性。类和接口数12,代码模块化程度尚可。每类平均方法数5.42,类的数量不多不少刚刚好符合规范并且可以看出类的职责较丰富。每方法平均语句数2.09,方法短小,符合代码规范。最复杂方法Main.main(),行数 9,复杂度 4最大块深度3,平均块深度 1.55代码嵌套不深,结构清晰。平均复杂度:1.05,复杂度少使得整体代码简单易于维护。从kiviat图上看各项指标均没有超出绿线部分,分布较为均匀没有出现极端异常。唯独需要注意点可能就是注释问题了。

踩坑与心得:

踩坑:这次大作业的难度很简单只需要管理好输出即可,但同时也有一个地方我踩坑好久了。关于计费重量的问题,当然不是计算计费重量出错了而是后面所有有关重量的问题都要是计费重量。如飞机有一个载重量,如果货物重量超过航班剩余载重量,程序输出The flight with flight number:航班号 has exceeded its load capacity and cannot carry the order.

按道理说比较的应该是货物的自身重量但却不想比较的计费重量,因此在这上面浪费了很多时间。

 

 

double totalweight = 0;
    for (int i = 0; i < goodsCount; i++) {
        String goodsId = input.nextLine();
        String goodsName = input.nextLine();
        double width = Integer.parseInt(input.nextLine());
        double length = Integer.parseInt(input.nextLine());
        double height = Integer.parseInt(input.nextLine());
        double weight = Integer.parseInt(input.nextLine());
        Goods goods = new Goods( goodsName,goodsId ,width, length, height, weight);
        totalweight += goods.getchargeweight(); --这一步添加的应该是计费重量
        goodsList.add(goods);
    }

 

修改完这个过后便正常通过了。不过我还是有一点想要吐槽一下ԅ(¯ㅂ¯ԅ),就是超重输出代码航班号后面还需要有一个空格!但是如果你是直接复制的话是没有空格的!当时我就是因为这个懵了还好当时室友就在旁边提醒了一下我不然可能就要纠结好久了。希望下一次能清楚点。

 

 心得:这一次大作业让我收获颇丰,首先便是让我对继承和多态的理解更深了,其次在这一次作业中我还首次使用了接口并且对于接口也有了一定理解。接口类似继承的部分作用,但与传统的类继承有区别。接口中定义的方法规范,实现类必须遵循,就像子类继承父类的方法并可重写一样。实现类要实现接口中的所有抽象方法,这保证了实现类具有接口所规定的行为和功能,类似于继承了接口的方法定义。不同在于类继承是单继承,一个类只能有一个直接父类;而接口是多实现,一个类可以同时实现多个接口,这使得类可以从多个不同的接口中获取规范,更灵活地组合不同的行为和功能。总结来说类继承通常用于表示 “是一种” 的关系,而接口更强调 “具有某种能力”。

改进建议:首先是需要改进费用计算逻辑,当前的费率计算策略比较简单,建议使用更灵活的设计模式。例如可以使用枚举来管理货物的费率。当然支付方式也可以进行修改使其更好,比如使用工厂模式来获得支付方式比我这里使用继承灵活一点。

interface PaymentMethod {
    String getPaymentWay();
}
class WechatPay implements PaymentMethod {
    public String getPaymentWay() {
        return "微信支付";
    }
}

 

第二次题目的设计与分析

题目要求:在上一次题目的基础上将货物分为普通货物、危险货物和加急货物三种,且对应的费率也不同。并且顾客也添加了顾客类型,不同的顾客类型有不同的优惠策略,支付方式也添加了现金支付一种。最后货物,顾客,支付方式必须可扩展。

设计类图:

 类的设计:

 

Main类:主类

public static void main(String[] args)

CustomerType类:接口类得到顾客类型

public void setType(String type)
public String gettype()

Customer类:获得顾客类,并实现CustomerType接口

private String name;
private String phone;
private String ID;
private String address;
private String customertype;
public Customer(String customertype,String name,String phone,String address,String id)
public void setphone(String phone)
public void setaddress(String address)
public void setID(String id)
public String getID()
public String getname()
public String getphone()
public String getaddress()
public String gettype()
public void setType(String type)

Cargo_objects类:抽象父类货物对象,获得运输货物的目标对象

复制代码
public void setname(String name)
public void setphone(String phone)
public void setaddress(String address)
public String getname()
public String getphone()
public String getaddress()
public Cargo_objects(String name,String phone,String address) 
复制代码

Consigner类:Cargo_objects类的子类,获得收件人

public Consigner(String name,String phone,String address)

Consignee类:Cargo_objects的子类,获得寄件人

public Consigner(String name,String phone,String address)

 Discount类:折扣接口,通过客户类型获得不同折扣

public double getDiscount(String type)

discountFactory类:折扣策略工厂类,通过客户类型获得不同的策略

public static Discount getStrategy(String customerType)

IndividualDiscount类:个人折扣类实现折扣接口

public double getDiscount(String type)

CorporateDiscount类:集团折扣类实现折扣接口

public double getDiscount(String type)

Flight类:航班类,获得航班信息

private String flightnum;
private String departureCity;
private String arrivalCity;
private int maxweight;
private LocalDate flightDate;
public Fight(String num, String departurecity, String arrivalcity, String date, int max)
public String getFlightnum() 
public void setFlightnum(String flightnum) 
public String getDepartureCity()
public void setDepartureCity(String departureCity) 
public String getArrivalCity() 
public void setArrivalCity(String arrivalCity)
public LocalDate getFlightDate() 
public void setFlightDate(LocalDate flightDate) 
public int getMaxweight() 
public void setMaxweight(int maxweight) 

GoodsType类:货物类型接口,设置获得货物类型

public void setType(String tytpe);
public String gettype();

Goods类:货物类实现GoodsType接口,封装存储所有货物的信息

private List<Goods> goods;
private Goods goodsinfo;
private Customer customer;
private Consigner consigner;
private Consignee consignee;
private int goodsnum = 0;
private String orderid;
private String orderDate;
public Goods(String type ,String goodsname, String Id, double length, double width, double height, double weight)
public List<Goods> getGoods()
public String gettype()
public void setType(String type)
public String getid() 
public void setorderid(String id) 
public void setdate(String date)
public String getDate()
public void setGoods(List<Goods> goods)
public Goods getGoodsinfo()
public void setGoodsinfo(Goods goodsinfo) 
public Customer getCustomer()
public void setCustomer(Customer customer)
public Consigner getConsigner()
public void setConsigner(Consigner consigner)
public Consignee getConsignee() 
public void setConsignee(Consignee consignee)
public int getGoodsnum() 
public void setGoodsnum(int goodsnum)
public void addgoods(Goods goods) 

Payment类:抽象父类,获得支付方法

abstract String getpayingway()

vxpay类:Payment类子类,微信支付方式

public String getpayingway()

alipay类:Payment类子类,支付宝支付方式

public String getpayingway()

cashpay类:Payment子类,现金支付方式

public String getpayingway()

Rate类:费率策略工厂,获得费率

public static RateStrategy getStrategy(double chargeWeight)

RateStrategy类:Rate类相关接口,定义根据货物类型获得费率方法

double getRate(double weight)

Low20类:计费重量小于20kg的策略实现

 public double getRate(double weight)

Low50类:计费重量小于50kg的策略实现

 public double getRate(double weight)

Low100类:计费重量小于100Kg的策略实现

 public double getRate(double weight)

Up100类:计费重量大于100kg的策略实现

 public double getRate(double weight)

Calculatemoney类:计算运费

private  Calculateweight weightCalculator = new Calculateweight();
public double calculateGoodsCost(Goods goods)
public double calculateTotalCost(List<Goods> goodsList) 

Calculateweight类:计算计费重量

private double VolumeWeight;
public double calculateVolumeWeight(double lenght,double width,double height)
public double dedetermineChargeWeight(Goods goods) 

GoodsTypeEnum类:货物类型枚举,集中管理费率,提高可扩展性

Normal(35, 30, 25, 15),   
Expedite(60, 50, 40, 30), 
Dangerous(80, 50, 30, 20); 
private  double[] rates; 
GoodsTypeEnum(double... rates) 
public double getRate(int index) 

报表分析:

代码规模:共 707 行,包含 288 条语句,方法调用语句 57 条,分支语句占比 3.5%,带注释的行占比 21.8%,注释有一定覆盖但仍有提升空间。结构复杂度:包含 19 个类和接口,平均每个类有 3.79 个方法,每个方法平均 2.06 条语句,整体结构较分散但方法粒度细。最复杂的方法是 discountFactory.getStrategy(),复杂度 3,最大代码块深度为 5,平均块深度 1.96,平均复杂度 1.08,多数代码逻辑简单,仅少数部分稍复杂,因此这点小问题不足挂齿。Kiviat 图:各指标(注释比例、类均方法数、方法均语句数、最大复杂度、最大深度等)分布较均衡,无极端偏高或偏低项,可以看出代码各方面质量较稳定。Block Histogram:深度为 1、2 的语句占比高,深度越大语句越少,这说明代码以浅层次逻辑为主,结构清晰易读。总结:代码结构相对简单,方法复杂度低,虽类和接口数量较多,但整体逻辑清晰。

踩坑与心得:

踩坑:由于这一次是在上一次的基础上进行迭代故而上一次出现的小问题这一次都没有再犯,算是吃一垫长一智了(再犯就不礼貌了|・ω・`))。

心得:这一次作业由于需要让客户货物和支付方式可扩展,并且还对客户和货物新引入了属性:类型,故而在添加属性时我使用了类型接口。这样做好处在于在新增加类型时我只需要添加相应类即可不需要大刀阔斧修改代码满足了开闭原则,并且我对新增加的获得折扣类不再是只用if-else来获得了,通过这几次的题目练习,我也基本掌握了策略工厂的应用虽然只是简单工厂。

以下是获得折扣的类和方法:

 1 interface Discount {
 2 public double getDiscount(String type);
 3 }
 4  class discountFactory {
 5     
 6      public static Discount getStrategy(String customerType) {
 7             switch (customerType) {
 8                 case "Individual":
 9                     return new IndividualDiscount();
10                 case "Corporate": 
11                     return new CorporateDiscount();
12                 default:
13                     throw new IllegalArgumentException();
14             }
15         }
16 } 
17 class IndividualDiscount implements Discount {
18  
19  public double getDiscount(String type) {
20      return 0.9;
21  }
22 }
23 class CorporateDiscount implements Discount {
24  public double getDiscount(String type) {
25      return 0.8;
26  }
27 }

 

 

 

 改进建议:

在费率策略工厂那里还可以进行继续优化,由于RateStrategy 依赖重量区间获取费率,未结合货物类型,故而我可以在 Goods 类中添加 GoodsTypeEnum 枚举字段,明确货物类型。并修改 RateStrategy 直接通过枚举获取费率。

interface RateStrategy {
    double getRate(GoodsTypeEnum type); //参数改为枚举类型
}
class low20 implements RateStrategy {
    public double getRate(GoodsTypeEnum type) {
        return type.getRate(0); //直接通过枚举获取费率
    }
}

这样一来我的策略工厂模式更加健壮,增强类型安全。

三、总结

这一次大作业,我应用了大量继承抽象和接口,并且还简单应用了策略工厂模式。从这一次的练习中,我已经熟悉了抽象继承和接口的使用,并且对策略工厂模式也有了大概的理解。虽然使用这些没有直接使用if-else简单,但在日后写代码时需要对代码进行多次迭代和扩展时,继承接口和策略工厂的好处便凸显出来,只能说现在的苦是为了以后有更多的甜做准备。虽然这一次大作业难度不大但是收获不少,希望下一次大作业也可以收获这么多,当然也希望难度不要太大了,真的不想去熬夜了(´×ω×`)。下一次blog见乾杯 []~( ̄▽ ̄)~*

posted on 2025-05-21 17:02  病故  阅读(29)  评论(0)    收藏  举报

导航