简单的面向对象(OO)练习

 

 

学生设备管理系统:

每个学校都有很多班级,每个班级都有很多设备。(设备可以更新)
每个设备都有购买价格,每种设备都有折旧率(如每年折旧10%)

  • 按班级进行统计,指定的班级有多少的设数量?
  • 按班级进行统计,当前时间,指定班级的设备价格总和?(考虑折旧)
  • 统计学校所有设备总数,所有设备价格总和?

那么接下来就是分步完成:

  • 学校类设计

首先是ClassRoom类:在第一步里面我们暂时只需要用到roomNumber这个属性。
我们默认的是教室编号只读,不能修改,所以封装的时候不需要set方法。
代码如下:

 1 public class ClassRoom {
 2     private String roomNumber;
 3 
 4     public ClassRoom(String roomNumber) {
 5         /**
 6          * 这里可以看到,在构造函数里面给roomNumber赋值之后,没有set方法,就无法修改了。
 7          * 这就是只读属性。
 8          */
 9         this.roomNumber = roomNumber;
10     }
11 
12     public String getRoomNumber() {
13         return roomNumber;
14     }
15 }

然后是School类,我们暂时有name和classRooms两个属性。其中对于classRooms的类型选择,我们有进一步的改进。
同时在School类构造函数里面对classRooms的实例化,也是符合思维习惯的一种方式。

 

 1 public class School {
 2     private String name; //name是只读的属性,只保留get方法。
 3     private List<ClassRoom> classRooms; //表示学校下面的一群班级。
 4 
 5     public School(String name){
 6         /**
 7          * 构造函数的作用一般都是初始化
 8          */
 9         this.name = name;
10         classRooms = new ArrayList<ClassRoom>(100);
11     }
12 
13     public String getName() {
14         return name;
15     }
16 
17 //    public void setName(String name) {
18 //        this.name = name;
19 //    }
20 
21     /**
22      * 这里的ClassRooms的set方法是不合理的,这样就失去了变化性。
23      * 所以删除set方法,增加添加和删除的两个方法。
24      * @return
25      */
26     public List<ClassRoom> getClassRooms() {
27         return classRooms;
28     }
29     public void addRoom(ClassRoom classRoom){
30         this.classRooms.add(classRoom);
31     }
32 
33     public void removeRoom(String roomNumber){
34         for (ClassRoom rm: classRooms) {
35             if(rm.getRoomNumber().equals(roomNumber)){
36                 classRooms.remove(rm);
37                 break;
38             }
39         }
40     }
41 }

 

 

代码里面的注释还是比较详细的。但是这里的问题就是,如果ClassRooms的数量比较大的时候,removeRoom使用for循环查找的方式就很慢了。所以可以改进一下,对ClassRooms使用Map的数据结构。下面是改进之后的:

 1 package edu.nuist.dev.biz;
 2 
 3 import java.util.HashMap;
 4 import java.util.Map;
 5 
 6 public class SchoolPro {
 7     private String name; //name是只读的属性,只保留get方法。
 8     private Map<String, ClassRoom> classRooms;//存储了教室和主键,优化查找速度。
 9 
10     public SchoolPro(String name) {
11         this.name = name;
12         classRooms = new HashMap<String, ClassRoom>(100);
13     }
14 
15     public String getName() {
16         return name;
17     }
18 
19     public Map<String, ClassRoom> getClassRooms() {
20         return classRooms;
21     }
22 
23     public void addRoom(ClassRoom classRoom) {
24         this.classRooms.put(classRoom.getRoomNumber(), classRoom);
25     }
26 
27     public void removeRoom(String roomNumber) {
28         this.classRooms.remove(roomNumber);
29     }
30 }

 

 

可以看到,改进之后,remove只需要一行代码就可以解决了,这里也说明了集合的重要性,对不同的情况作最合适的选择。
最后,我们在ui包内新建一个SchoolTest的类来测试一下,阶段性的测试,可以防止最后各种问题的堆砌,同时可以清理一遍思路,是必须掌握的一个手段。

 

 1 public class SchoolTest {
 2     public static void main(String[] args) {
 3         SchoolPro school = new SchoolPro("南京信息工程大学");
 4         ClassRoom room1 = new ClassRoom("101");
 5         ClassRoom room2 = new ClassRoom("102");
 6         ClassRoom room3 = new ClassRoom("103");
 7         ClassRoom room4 = new ClassRoom("104");
 8         ClassRoom room5 = new ClassRoom("105");
 9         school.addRoom(room1);
10         school.addRoom(room2);
11         school.addRoom(room3);
12         school.addRoom(room4);
13         school.addRoom(room5);
14         Map<String, ClassRoom> classRoomMap = school.getClassRooms();
15         Set<String> keySet = classRoomMap.keySet();
16         //根据主键来循环
17         for (String key : keySet) {
18             System.out.println(key);
19         }
20 
21         //移除操作
22         System.out.println("移除103-------------");
23         classRoomMap.remove("103");
24         Map<String, ClassRoom> classRoomMap2 = school.getClassRooms();
25         Set<String> keySet2 = classRoomMap2.keySet();
26         for (String key : keySet2) {
27             System.out.println(key);
28         }
29     }
30 }

 

 

  • 设备类设计

要求的是每个教室有多个设备,所以我们首先定义一个Device的abstract类,写上通用的属性方法

 1 public abstract class Device {
 2     private String deviceNo;    //设备编号
 3     private double buyPrice;    //购买价格
 4     private double nowPrice;    //当前价格
 5     private double oldRate;     //折旧率
 6     private String deviceType;  //设备类型
 7 
 8     public Device(String deviceNo,String deviceType){
 9         this.deviceNo = deviceNo;
10         this.deviceType = deviceType;
11     }
12 
13     public String getDeviceNo() {
14         return deviceNo;
15     }
16 
17     public double getBuyPrice() {
18         return buyPrice;
19     }
20 
21     public void setBuyPrice(double buyPrice) {
22         this.buyPrice = buyPrice;
23     }
24 
25     public double getNowPrice() {
26         return nowPrice;
27     }
28 
29     public void setNowPrice(double nowPrice) {
30         this.nowPrice = nowPrice;
31     }
32 
33     public double getOldRate() {
34         return oldRate;
35     }
36 
37     public void setOldRate(double oldRate) {
38         this.oldRate = oldRate;
39     }
40 }

 

 

这里要注意的是,我们有一个设备类型,是通过新建一个DeviceType类,在其中定义常量的方法来实现的,根据之后功能的不同,也可以通过接口实现。
在构造方法中加入DeviceType,子类继承实现构造方法时,相当于告诉你,我是Computer,或者是Table。
我们这里假设教室里面的设备有3中,Computer,Table,Chair

 1 //Computer类
 2 package edu.nuist.dev.biz;
 3 public class Computer extends Device{
 4 
 5     public Computer(String deviceNo) {
 6         super(deviceNo,DeviceType.COMPUTER);
 7     }
 8 }
 9 //Chair类
10 package edu.nuist.dev.biz;
11 public class Chair extends Device{
12 
13     public Chair(String deviceNo) {
14         super(deviceNo,DeviceType.CHAIR);
15     }
16 }
17 //Table类
18 package edu.nuist.dev.biz;
19 public class Table extends Device{
20 
21     public Table(String deviceNo) {
22         super(deviceNo,DeviceType.TABLE);
23     }
24 }

 

 

同样的,在classRoom类中,我们要管理多个设备,类似于一个学校的多个教室一样,用Map结构来定义。并添加add和remove方法。和之前的类似,不再重复给出代码。
到这里,我们的第二步就完成了

  • 统计教室的设备信息

添加一个DeviceTest类来统计

 1 public class DeviceTest {
 2     public static void main(String[] args){
 3         //新建设备,电脑、桌子、椅子各5个。
 4         Computer computer1 = new Computer("C001");
 5         Computer computer2 = new Computer("C002");
 6         Computer computer3 = new Computer("C003");
 7         Computer computer4 = new Computer("C004");
 8         Computer computer5 = new Computer("C005");
 9         Table table1 = new Table("t001");
10         Table table2 = new Table("t002");
11         Table table3 = new Table("t003");
12         Table table4 = new Table("t004");
13         Table table5 = new Table("t005");
14         Chair chair1 = new Chair("ch001");
15         Chair chair2 = new Chair("ch002");
16         Chair chair3 = new Chair("ch003");
17         Chair chair4 = new Chair("ch004");
18         Chair chair5 = new Chair("ch005");
19         //新建两个教室
20         ClassRoom classRoom1 = new ClassRoom("101");
21         ClassRoom classRoom2 = new ClassRoom("202");
22 
23         //给教室101添加设备
24         classRoom1.addDevice(computer1);
25         classRoom1.addDevice(computer2);
26         classRoom1.addDevice(computer3);
27         classRoom1.addDevice(table1);
28         classRoom1.addDevice(table2);
29         classRoom1.addDevice(chair1);
30         classRoom1.addDevice(chair2);
31         classRoom1.addDevice(chair3);
32         //给教室202添加设备,添加重复的设备
33         classRoom2.addDevice(computer1);
34         classRoom2.addDevice(computer4);
35         classRoom2.addDevice(computer5);
36         classRoom2.addDevice(table3);
37         classRoom2.addDevice(table4);
38         classRoom2.addDevice(table5);
39         classRoom2.addDevice(table3);
40         classRoom2.addDevice(chair2);
41         classRoom2.addDevice(chair4);
42         classRoom2.addDevice(chair5);
43 
44         //获取设备信息
45         Map<String,Device> allDevice1 = classRoom1.getDevices();
46         System.out.println(classRoom1.getRoomNumber() + "共有设备:" + allDevice1.size());
47         //列出设备
48         Set<String> keys1 = allDevice1.keySet();
49         for (String key:keys1){
50             Device dev = allDevice1.get(key);
51             System.out.println(dev.getDeviceType() + "---" +dev.getDeviceNo());
52         }
53 
54         Map<String,Device> allDevice2 = classRoom2.getDevices();
55         System.out.println(classRoom2.getRoomNumber() + "共有设备:" + allDevice2.size());
56         Set<String> keys2 = allDevice2.keySet();
57         for (String key:keys2){
58             Device dev = allDevice2.get(key);
59             System.out.println(dev.getDeviceType() + "---" +dev.getDeviceNo());
60         }
61     }
62 }

 

 

这里有注意点:一台设备只能被分配到一个教室,所以我们设置了一个isUsed来判断它在添加时的状态。测试之后发现判断重复是可以的。到此我们解决了第一个问题。

 1 public void addDevice(Device device){
 2         //表示Device不为空,并且isUsed为false,才可以添加。
 3         if ( device != null){
 4             if (!device.isUsed()){
 5                 devices.put(device.getDeviceNo(),device);
 6                 device.setUsed(true);//改变状态
 7             }else {
 8                 System.out.println(device.getDeviceNo() + "已经被分配了,无法使用");
 9             }
10         }else {
11             System.out.println("设备不存在");
12         }
13     }

 

 

  • 统计设备折旧后的价格

首先我们要知道折旧后的价格:
nowPrice = buyPrice - buyPrice * dayOldRate * days
dayOldRate = oldRate / 365
days = nowDate - buyDate
然后在Device类中对getNowPrice修改

1 public double getNowPrice() {
2         double dayOldRate = this.oldRate / 365;
3         long pastTime = new Date().getTime() - this.buyDate.getTime();
4         long days = pastTime / (3600 * 1000 * 24);
5         this.nowPrice = this.buyPrice - this.buyPrice * dayOldRate * days;
6         return nowPrice;
7     }

 

 

然后我们设计一个RateTest类来测试,期间采用的方法是先写第一个,然后设置断点去调试看看nowPrice是否正确,在正确之后再补写其他的,这样就减少了不必要的错误检查。

 1 public class RateTest {
 2     public static void main(String[] args){
 3         try {
 4             SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd");
 5             //新建设备,电脑、桌子、椅子各5个。
 6             Computer computer1 = new Computer("C001");
 7             computer1.setOldRate(0.1);
 8             computer1.setBuyDate(sd.parse("2015-03-12"));
 9             computer1.setBuyPrice(3800);
10             //double a = computer1.getNowPrice(); 设置断点处来查看nowPrice是否计算正确。
11             Computer computer2 = new Computer("C001");
12             computer2.setOldRate(0.1);
13             computer2.setBuyDate(sd.parse("2016-07-08"));
14             computer2.setBuyPrice(3800);
15             Table table1 = new Table("t001");
16             table1.setOldRate(0.125);
17             table1.setBuyDate(sd.parse("2013-08-10"));
18             table1.setBuyPrice(280);
19             Chair chair1 = new Chair("ch001");
20             chair1.setBuyDate(sd.parse("2015-10-01"));
21             chair1.setBuyPrice(300);
22             chair1.setOldRate(0.2);
23 
24             ClassRoom classRoom1 = new ClassRoom("101");
25             classRoom1.addDevice(computer1);
26             classRoom1.addDevice(computer2);
27             classRoom1.addDevice(table1);
28             classRoom1.addDevice(chair1);
29 
30             Map<String,Device> allDevice = classRoom1.getDevices();
31             Set<String> keys = allDevice.keySet();
32             System.out.println(classRoom1.getRoomNumber() + "共有设备:" + allDevice.size());
33             double allPrice = 0;
34             for (String key:keys){
35                 Device dev = allDevice.get(key);
36                 double nowPrice = dev.getNowPrice();
37                 allPrice += nowPrice;
38                 System.out.println(dev.getDeviceNo() + "---" + dev.getDeviceType() + "---" +dev.getNowPrice());
39             }
40             System.out.println(classRoom1.getRoomNumber() + "当前教室的设备总价格:" + allPrice);
41 
42         }catch (Exception e){
43             e.printStackTrace();
44         }
45     }
46 }

 

  • 重写计算设备的价格

我们考虑到实际的情况,电脑的折旧率根据实际情况,波动很大,所以我们需要在Computer类中增加一个权值。利用重写来实现多态。

 1 public class Computer extends Device{
 2     private double value;   //价格计算的加权值
 3 
 4     public Computer(String deviceNo) {
 5         super(deviceNo,DeviceType.COMPUTER);
 6         this.value = 1;
 7     }
 8 
 9     public double getNowPrice(){
10         this.nowPrice = super.getNowPrice() * value;
11         return this.nowPrice;
12     }
13 
14     public double getValue() {
15         return value;
16     }
17 
18     public void setValue(double value) {
19         this.value = value;
20     }
21 }

 

 

加上权值之后,价格的变化就更符合实际情况。
至此,我们就完成了问题二。

  • 统计学校设备总价

我们的过程就是:学校->班级->设备->价格。依次寻找。
添加测试类

 1 package edu.nuist.dev.ui;
 2 
 3 import edu.nuist.dev.biz.*;
 4 
 5 import java.text.SimpleDateFormat;
 6 import java.util.Map;
 7 import java.util.Set;
 8 
 9 public class AllDeviceTest {
10     public static void main(String[] args){
11         try {
12             SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd");
13             Computer computer1 = new Computer("C001");
14             computer1.setOldRate(0.1);
15             computer1.setBuyDate(sd.parse("2015-03-12"));
16             computer1.setBuyPrice(3800);
17             computer1.setValue(1.15);
18             Computer computer2 = new Computer("C002");
19             computer2.setOldRate(0.1);
20             computer2.setBuyDate(sd.parse("2016-07-08"));
21             computer2.setBuyPrice(3800);
22             computer2.setValue(1.4);
23             Table table1 = new Table("t001");
24             table1.setOldRate(0.125);
25             table1.setBuyDate(sd.parse("2013-08-10"));
26             table1.setBuyPrice(280);
27             Chair chair1 = new Chair("ch001");
28             chair1.setBuyDate(sd.parse("2015-10-01"));
29             chair1.setBuyPrice(300);
30             chair1.setOldRate(0.2);
31 
32             ClassRoom classRoom1 = new ClassRoom("101");
33             classRoom1.addDevice(computer1);
34             classRoom1.addDevice(computer2);
35             classRoom1.addDevice(table1);
36             classRoom1.addDevice(chair1);
37 
38             ClassRoom classRoom2 = new ClassRoom("202");
39             Computer computer3 = new Computer("C003");
40             computer3.setOldRate(0.1);
41             computer3.setBuyDate(sd.parse("2016-11-29"));
42             computer3.setBuyPrice(3900);
43             computer3.setValue(1.2);
44             classRoom2.addDevice(computer3);
45 
46             SchoolPro school = new SchoolPro("南京信息工程大学");
47             school.addRoom(classRoom1);
48             school.addRoom(classRoom2);
49 
50             Map<String,ClassRoom> allRooms = school.getClassRooms();
51             Set<String> roomKeys = allRooms.keySet();
52             int allDeviceCount = 0;
53             double allDevicePrice = 0;
54             for (String key:roomKeys){
55                 ClassRoom classRoom = allRooms.get(key);
56                 Map<String,Device> allDevice = classRoom.getDevices();
57                 allDeviceCount += allDevice.size();
58                 Set<String> devKeys = allDevice.keySet();
59                 for (String dev:devKeys){
60                     Device device = allDevice.get(dev);
61                     allDevicePrice += device.getNowPrice();
62                 }
63             }
64             System.out.println(school.getName() + "所有设备总数:" + allDeviceCount);
65             System.out.println(school.getName() + "所有设备总价格:" + allDevicePrice);
66 
67         }catch (Exception e){
68             e.printStackTrace();
69         }
70     }
71 }

 

 

通过一个简单的例子,我们大致了解了学校的设备的个数和总价格。
到此,我们解决了问题三。

https://pan.baidu.com/s/1pNr8hlx

查看可执行源码。

总结

简单的OO练习,涵盖了继承多态的主要思想,结合实际的例子,加深了对面向对象的理解。同时,一些代码的规范,技巧,也是值得反复揣摩学习的。

Alkane 2018-01-21 23:35:23

posted @ 2018-01-21 23:38  Alkane  阅读(348)  评论(0编辑  收藏  举报