一、前言
關於傢俱電路系統和答題判題系統我認為最主要的難點在於類設計,我們先需要設計好父類再對子類和具體方法進行補充,就拿電路系統舉例,無論是受控裝置還是控制裝置,具體程式碼都較為簡單,只需簡單的數學運算就能求出具體功率,但要如何將裝置加入電路當中以及能夠進行計算才是較為重要的,所以我們要熟悉list類的使用,能夠正確的使用多型和繼承,並且我們要有能根據具體情況去設計類的能力(也就是所謂的先研究需求再設計),再說到題目上,第4次題目集(答題判題系統)我認為較難,需要處理的類較多,而第5,6次題目集(傢俱電路系統)由於迭代不多且類設計較為清晰,難度則適中,但需要在意具體實現的程式碼。
二、設計與分享
第4次題目集
點選檢視程式碼
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.ArrayList;
class QuestionMgr {
private ArrayList<Question> questions = new ArrayList<>();
public boolean isExist(int id){
for(int i=0;i<questions.size();i++){
if(questions.get(i).getId() == id)
return true;
}
return false;
}
public void addQuestion(int id, String content,String answer){
questions.add(new Question(id,content,answer));
}
public void setInvalid(int id){
for(int i=0;i<questions.size();i++){
if(questions.get(i).getId() == id)
questions.get(i).setValid(false);
}
}
public String getCorrectAnswer(int id){
for(int i=0;i<questions.size();i++){
if(questions.get(i).getId() == id)
return questions.get(i).getAnswer();
}
return null;
}
public boolean isValid(int id){
for(int i=0;i<questions.size();i++){
if(questions.get(i).getId() == id)
return questions.get(i).isLive();
}
return false;
}
public String getContentById(int id){
for(int i=0;i<questions.size();i++){
if(questions.get(i).getId() == id)
return questions.get(i).getContent();
}
return null;
}
}
class Question {
private String content;
private int id;
private boolean live;
private String answer;
public Question(int id ,String content, String answer) {
this.content = content;
this.id = id;
this.live = true;
this.answer = answer;
}
public int getId(){
return id;
}
public boolean isLive(){
return live;
}
public String getAnswer() {
return answer;
}
public void setValid(boolean f){
this.live = f;
}
public String getContent() {
return content;
}
}
class TestPaper {
private int paperNumber;
private int questionnumber;
private ArrayList<Integer> paperQuestions = new ArrayList<>();
private ArrayList<Integer> paperScore = new ArrayList<>();
public TestPaper(int paperNumber) {
this.paperNumber = paperNumber;
questionnumber = 0;
}
public int getQuestionNumber(){
return questionnumber;
}
public void addPaperQuestion(int number,int score){
paperQuestions.add(number);
paperScore.add(score);
questionnumber++;
}
public int getPaperNumber() {
return paperNumber;
}
public int getQuestionIdByIndex(int index){
return paperQuestions.get(index);
}
public int getScoreByIndex(int index){
return paperScore.get(index);
}
public boolean isFulll(){
int sum = 0;
for(int i=0;i<paperScore.size();i++)
sum += paperScore.get(i);
if(sum == 100)
return true;
else
return false;
}
public ArrayList<Integer> getQuestions() {
return paperQuestions;
}
public ArrayList<Integer> getQuestionScores() {
return paperScore;
}
}
class TestPaperMgr{
private ArrayList<TestPaper> paperMgr = new ArrayList<>();
public TestPaper getPaper(int id){
for(int i=0;i<paperMgr.size();i++){
if(paperMgr.get(i).getPaperNumber() == id)
return paperMgr.get(i);
}
return null;
}
public void updatePaper(TestPaper paper){
for(int i=0;i<paperMgr.size();i++){
if(paperMgr.get(i).getPaperNumber() == paper.getPaperNumber()){
paperMgr.remove(i);
break;
}
}
paperMgr.add(paper);
}
}
class StudentMgr{
ArrayList<Student> allStudents = new ArrayList<>();
public String getStudentNameById(String id){
for(int i=0;i<allStudents.size();i++){
if(allStudents.get(i).getId().equals(id))
return allStudents.get(i).getName();
}
return null;
}
public void addStudents(ArrayList<Student> stus){
for(int i=0;i<stus.size();i++)
allStudents.add(stus.get(i));
}
}
class Student {
private String name;
private String id;
public Student(String id, String name) {
this.id = id;
this.name = name;
}
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class AnswerSheet {
private TestPaper testPaper;
private QuestionMgr questionMgr;
private ArrayList<Integer> questionsOder = new ArrayList<>();
private ArrayList<String> answers = new ArrayList<>();
public AnswerSheet(TestPaper testPaper,QuestionMgr mgr) {
this.testPaper = testPaper;
this.questionMgr = mgr;
}
public void addAnswer(int index,String ans){
questionsOder.add(index);
answers.add(ans);
}
public void outputResults(String studentId, String studentName) {
if (!testPaper.isFulll()) {
System.out.println("alert: full score of test paper" + testPaper.getPaperNumber() + " is not 100 points");
}
int cnt = 0;
int sumScore = 0;
String strScore = "";
if(studentName == null)
strScore = studentId + " not found";
else
strScore = studentId+" "+ studentName+":";
ArrayList<String> logs = new ArrayList<>();
int ok = 0,err=0;
for(int i=0;i<questionsOder.size();i++){
int index = questionsOder.get(i);
String ans = answers.get(i);
if(index > testPaper.getQuestionNumber())
continue;
cnt++;
int questionId = testPaper.getQuestionIdByIndex(index-1);
int questionScore = testPaper.getScoreByIndex(index-1);
if(!questionMgr.isExist(questionId)){
System.out.println("non-existent question~0");
}else{
if(!questionMgr.isValid(questionId)){
String ls = "the question "+ String.valueOf(index) +" invalid~0";
logs.add(ls);
}else{
String right = questionMgr.getCorrectAnswer(questionId);
if(right.equals(ans)){
System.out.println(questionMgr.getContentById(questionId)+"~"+ans+"~true");
strScore = strScore +" " + String.valueOf(questionScore);
sumScore += questionScore;
ok++;
}else{
System.out.println(questionMgr.getContentById(questionId)+"~"+ans+"~false");
strScore = strScore +" 0";
err++;
}
}
}
}
for(int i=0;i<logs.size();i++){
strScore = strScore +" 0";
err++;
System.out.println(logs.get(i));
}
for(int i = (ok+err);i<testPaper.getQuestionNumber();i++)
strScore = strScore +" 0";
if(cnt < testPaper.getQuestionNumber())
System.out.println("answer is null");
if(studentName == null)
System.out.println(strScore);
else{
strScore = strScore + "~"+String.valueOf(sumScore);
System.out.println(strScore);
}
}
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
TestPaperMgr paperMgr = new TestPaperMgr();
QuestionMgr questionMgr = new QuestionMgr();
StudentMgr stuMgr = new StudentMgr();
String studentId="";
String studentName = "";
TestPaper currentpaper = null;
AnswerSheet answerSheet = null;
while (true) {
String line = scanner.nextLine();
if (line.equals("end")) {
break;
}
Matcher matcher;
if ((matcher = Pattern.compile("#N:(\\d+) #Q:(.*?) #A:(\\d+)").matcher(line)).matches()) {
int number = Integer.parseInt(matcher.group(1));
String questionContent = matcher.group(2);
String answer = matcher.group(3);
questionMgr.addQuestion(number, questionContent, answer);
} else if ((matcher = Pattern.compile("#T:(\\d+) (.+)").matcher(line)).matches()) {
int paperNumber = Integer.parseInt(matcher.group(1));
TestPaper paper = paperMgr.getPaper(paperNumber);
if(paper == null){
paper = new TestPaper(paperNumber);
}
String[] parts = matcher.group(2).split(" ");
for(int i=0;i<parts.length;i++){
String[] pt = parts[i].split("-");
int questionNumber = Integer.parseInt(pt[0]);
int score = Integer.parseInt(pt[1]);
paper.addPaperQuestion(questionNumber, score);
}
paperMgr.updatePaper(paper);
} else if ((matcher = Pattern.compile("#X:(\\d+) (.+)").matcher(line)).matches()) {
int index = line.indexOf(":");
line = line.substring(index+1);
String[] parts = line.split("-");
ArrayList<Student> stus = new ArrayList<>();
for(int i=0;i<parts.length;i++){
String[] stu = parts[i].split(" ");
if(stu.length==2){
stus.add(new Student(stu[0],stu[1]));
}
}
stuMgr.addStudents(stus);
} else if ((matcher = Pattern.compile("#S:(\\d+) (\\d+) (.+)").matcher(line)).matches()) {
int paperNmb = Integer.parseInt(matcher.group(1)); //獲取試卷編號
currentpaper = paperMgr.getPaper(paperNmb); //根據試卷編號找到試卷
if(currentpaper != null){
studentId = matcher.group(2);
studentName = stuMgr.getStudentNameById(studentId);
String[] ques = matcher.group(3).split(" ");
answerSheet = new AnswerSheet(currentpaper,questionMgr);
for(int i=0;i<ques.length;i++){
String ans = ques[i].substring(ques[i].indexOf(":")+1);
String[] pts = ans.split("-");
if (pts.length == 2){
int questionNumber = Integer.parseInt(pts[0]);
String answer = pts[1];
answerSheet.addAnswer(questionNumber,answer);
}
}
}
}else if(line.startsWith("#D:N-")){
int index = line.indexOf("-");
if(index < line.length()){
int id = Integer.parseInt(line.substring(index+1));
questionMgr.setInvalid(id);
}
} else {
System.out.println("wrong format:"+line);
}
}
if(currentpaper == null){
System.out.println("The test paper number does not exist");
}else {
answerSheet.outputResults(studentId, studentName);
}
}
}
主要的類有Testpaper(試卷類)Answersheet(答卷類)Question(問題類)Student(學生類),其餘的mgr都是負責正規表示式收集資訊然後再管理,逐個分析學生類是最為簡單的,只需要學生個人資訊及答題分數(至於判題的具體實現甚至都不是在學生類中實現),再是問題類,它關乎答卷類及試卷類,試卷類中需要儲存問題的資訊,而答卷類則要有問題的標準答案,(這樣看其實前兩個類本身的設計也並不複雜,重點在於類間的關係)但本身類中則是最基礎的get及set甚至不需要立馬完成,Test及Answer我們放在一起看分析,可以看到兩個類都需要用到兩個Arraylist去儲存資訊,Test透過題號去新增問題,而Answer也是透過題號來進行比較答案並且負責記錄將答題的資訊給到學生。
第5,6次題目集
點選檢視程式碼
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.text.DecimalFormat;
class Circuit {
protected static LinkedHashMap<String,Device> deviceMap = new LinkedHashMap<>();
public Circuit() {
}
public void update() {
Device previous =null;
for(Device device : deviceMap.values()) {
if(previous!=null) {
device.setV1(previous.getV2());
}
if(device instanceof Switch) {
((Switch) device).update();
}else if(device instanceof Gearspeed) {
((Gearspeed) device).update();
}else if(device instanceof Unitgear) {
((Unitgear) device).update();
}else if(device instanceof Light1) {
((Light1) device).update();
}else if(device instanceof Light2) {
((Light2) device).update();
}else if(device instanceof Fan) {
((Fan) device).update();
}
previous=device;
}
}
public void getDeviceStates() {
for (Map.Entry<String, Device> entry : deviceMap.entrySet()) {
System.out.print("@" + entry.getKey() + ":");
entry.getValue().display();
}
}
}
class Device {
protected double v1;
protected double v2;
public Device() {}
public Device(double v1,double v2) {
this.v1=0;
this.v2=0;
}
public double getV1() {
return v1;
}
public void setV1(double v1) {
this.v1 = v1;
}
public double getV2() {
return v2;
}
public void setV2(double v2) {
this.v2 = v2;
}
public void display() {
}
}
class device1 extends Device {
public device1(double v1,double v2) {
this.v1=v1;
this.v2=v2;
}
public device1() {
}
}
class device2 extends Device{
protected double v;
public device2() {
}
public device2(double v1,double v2,double v) {
this.v1=v1;
this.v2=v2;
this.v=v;
}
}
class Fan extends device2{
private double lux=0;
public Fan(double v,double v1,double v2,double lux) {
super(v,v1,v2);
this.lux=0;
}
public Fan() {
}
public double getLux() {
return lux;
}
public void setlux(double lux) {
this.lux=lux;
}
public void update() {
if(this.v1<80) {
this.lux=0;
}
if(this.v1>=80&&this.v1<=150) {
this.lux=4*this.v1-240;
}
if(this.v1>=150) {
this.lux=360;
}
}
public void display() {
System.out.println(Math.round(this.lux));
}
}
class Gearspeed extends device1{
private double v1 = 220;
private int gear=0;
public Gearspeed() {
}
public Gearspeed(int gear,double v1,double v2) {
super(v1,v2);
this.gear=gear;
this.gear=0;
}
public int getGear() {
return gear;
}
public void up() {
if(this.gear<3)
this.gear++;
}
public void down() {
if(this.gear>0)
this.gear--;
}
public void update() {
if (this.v1 == 0) {
this.v2 = 0;
} else {
switch (this.gear) {
case 0:
this.v2 = 0;
break;
case 1:
this.v2 = 0.3 *this.v1;
break;
case 2:
this.v2 = 0.6 *this.v1;
break;
case 3:
this.v2 = 0.9 * this.v1;
break;
default:
this.v2 = 0;
break;
}
}
}
public void display() {
System.out.println(this.gear);
}
}
class Light1 extends device2{
private double v2 = 0;
private double lux=0;
public Light1(double v,double v1,double v2,double lux) {
super(v,v1,v2);
this.lux=0;
}
public Light1() {
}
public double getLux() {
return lux;
}
public void update() {
if(this.v1>=0&&this.v1<=9) {
this.lux=0;
}else if(this.v1>9&&this.v1<=220) {
this.lux=(5.0/7.0)*v1+(300.0/7.0);
}else if(this.v1>220) {
this.lux=200;
}
}
public void display() {
System.out.println((int)(this.lux));
}
}
class Light2 extends device2{
private double v2 = 0;
private double lux=0;
public Light2(double v,double v1,double v2,double lux) {
super(v,v1,v2);
this.lux=0;
}
public Light2() {
}
public double getLux() {
return lux;
}
public void update() {
if(this.v1>0) {
this.lux=180;
}
else if(this.v1==0) {
this.lux=0;
}
}
public void display() {
System.out.println((int)this.lux);
}
}
class Switch extends device1{
private int state;
public Switch() {
}
public Switch(double v1,double v2,int state) {
super(v1,v2);
this.state=0;
}
public int getState() {
return this.state;
}
public void setState() {
if (this.state==1) {
this.state=0;
}else if(this.state==0) {
this.state=1;
}
}
public void update() {
if(this.state==1) {
this.v2=this.v1;
}else if(this.state==0) {
this.v2=0;
}
}
public void display() {
if(this.state==0) {
System.out.println("tuened on");
}else if(this.state==1) {
System.out.println("closed");
}
}
}
class Unitgear extends device1{
private double v1 = 220;
private double gear=0;
public Unitgear() {
}
public Unitgear(double gear,double v1,double v2) {
super(v1,v2);
if(gear>=0&&gear<=1)
this.gear=gear;
this.gear=0;
}
public double getGear() {
return gear;
}
public void setGear(double gear) {
if(gear>=0&&gear<=1)
this.gear = gear;
}
public void update() {
this.v2=((double)this.v1*this.gear);
}
public void display() {
DecimalFormat decimalFormat = new DecimalFormat("#0.00");
String format = decimalFormat.format(this.gear);
System.out.println(format);
}
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
Circuit circuit = new Circuit();
Device device = new Device();
while(true) {
String input = scanner.next();
if(input.startsWith("[")) {
String regex = "[A-Z]\\d+-2";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(input);
while(matcher.find()) {
String inputs = input.substring(1,3);
if(inputs.startsWith("K")) {
Device switchs = new Switch();
Circuit.deviceMap.put(inputs, switchs);
}else if(inputs.startsWith("F")) {
Device gears = new Gearspeed();
circuit.deviceMap.put(inputs, gears);
}else if(inputs.startsWith("L")) {
Device unit = new Unitgear();
circuit.deviceMap.put(inputs, unit);
}else if(inputs.startsWith("B")) {
Device light1 = new Light1();
circuit.deviceMap.put(inputs, light1);
}else if(inputs.startsWith("R")) {
Device light2 = new Light2();
circuit.deviceMap.put(inputs, light2);
}else if(inputs.startsWith("D")) {
Device fan = new Fan();
Circuit.deviceMap.put(inputs, fan);
}
if(Circuit.deviceMap.size()==1){Circuit.deviceMap.get(inputs).setV1(220);}
}
}else if(input.startsWith("#K")) {
for(Map.Entry<String,Device>entry : circuit.deviceMap.entrySet()) {
if(entry.getKey().startsWith("K")) {
((Switch) entry.getValue()).setState();
break;
}
}
}else if(input.startsWith("#F")) {
for(Map.Entry<String,Device>entry : circuit.deviceMap.entrySet()) {
if(entry.getKey().startsWith("F")) {
if(input.substring(3,4).equals("+")) {
((Gearspeed) entry.getValue()).up();
}else if(input.substring(3,4).equals("-")) {
((Gearspeed) entry.getValue()).down();
}
break;
}
}
}else if(input.startsWith("#L")) {
for(Map.Entry<String,Device>entry : circuit.deviceMap.entrySet()) {
if(entry.getKey().startsWith("L")) {
double input1 = Double.parseDouble(input.substring(4));
((Unitgear) entry.getValue()).setGear(input1);
break;
}
}
}else if(input.equals("end")) {
break;
}
}
circuit.update();
circuit.getDeviceStates();
}
}
對於傢俱電路系統,我認為只需要細說Device(裝置)及Circuit(電路)類即可,對於具體的受控裝置和控制裝置其程式碼都是數學公式或者調整狀態的於總結而言並不重要,基本都是update()去更新資訊dispaly()去展示資訊,對於Device我設定了兩個子類1和2,分別對應控制及受控裝置,這樣做的好處是在電路連線時你可以清楚的錄入裝置並分別進行判斷,並且在電路中可以更快速的判斷前後的電壓,而電路類我其實一直沒想明白所以只寫了串聯電路,在第五次題目集中我透過for迴圈遍歷list陣列再判斷為什麼類新增進電路當中,但這是因為只有串聯電路,以至於並聯電路沒有解決,但我的想法是當讀到需要並聯的裝置時則再new一個電路以此來達到並聯的目的,可這只是個想法再加上此次有電阻我沒有想好要怎麼將電壓在整個電路中進行分壓計算,導致沒有做出第六次題目集。我個人覺得電路會比答題系統簡單,因為答題系統要負責的父類較多這就導致關係較為複雜,雖然電路有很多子類但之間的耦合性不高而且極其方便更改,重點只要考慮裝置與電路關係即可。
三、採坑心得
首先我想說的就是關於父類及子類的問題,在電路中我在父類Device中定義了每個電壓都為0,然後在子類裝置super(),以此希望覆蓋掉再重新接入一個電壓,但是這是不行的並且導致了我在寫完程式碼執行時發現,無論如何受控裝置的功率都為0,我根本沒注意到之前的設計有問題,不管怎麼除錯都顯示控制裝置傳遞電壓到下一步的受控裝置都為0,我一直以為是我電路連線時有問題畢竟這樣看起來像是傳遞電壓出了錯誤,我還是在複寫裝置類時偶然更改才發現的,子類的複寫無法覆蓋掉父類;
還有就是在需求分析時可以嘗試去用紙筆規範的記錄下然後完整的瀏覽思考一遍最起碼對我個人而言會讓我對於類的設計更加清晰一些;
再說一個關於list使用的問題,LinkedHashMap是一個有序的,並且你可以根據accessOrder來控制節點以訪問元素,並且值得一提的是它可以根據訪問順序或插入順序來訪問元素,這種雙向連結串列查詢效率快且維護插入順序,我個人認為在需要按順序去解決問題的情景下較為好用,當然如果是答題判題程式,是按我這種序號來對比的情況就不需要用到,但也可以根據順序來逐一比較(僅憑個人喜好選擇);
關於介面,其實這是一個非常好用的點,可以幫助減少程式碼冗餘,我要說的是要注意介面作為一個抽象類,它是要被類實現的而非被類繼承,當我們寫非抽象類如果實現了結果一定要實現介面中的所以方法的具體實現。
四、改進建議
1.減少型別檢查與利用多型性:
當前在Circuit類的update方法中使用了大量的instanceof型別檢查來判斷裝置的具體型別。這不僅違反了開閉原則,也使得程式碼難以維護。一個更好的做法是定義一個通用的update方法在Device介面中,並讓各個子類自行實現其特有的更新邏輯。這樣一來,Circuit類在呼叫update方法時無需關心裝置的具體型別,只需呼叫介面定義的方法即可。
2.引入管理器:
使用管理類以方便操作例如目前Circuit類直接管理了所有的裝置,但隨著裝置數量的增加,這種管理方式可能會變得難以維護。因此,準備引入一個專門的CircuitManager類來負責裝置的新增、刪除和更新操作。CircuitManager類可以維護一個裝置列表,並提供相應的方法來操作這些裝置。這樣,Circuit類可以專注於其本身的邏輯,而裝置的管理則由CircuitManager類負責。以此邏輯去完善電路。
封裝裝置處理邏輯:
為了提高程式碼的可讀性和可維護性,可以將不同裝置的處理邏輯封裝到單獨的方法中。例如,可以將開關裝置的狀態切換邏輯封裝到switchState方法中,將調速裝置的檔位調整邏輯封裝到adjustSpeed方法中。這樣一來,每個裝置類都只需要關注其特有的邏輯,而無需處理其他裝置的邏輯。同時,這也使得程式碼更加模組化,方便進行迭代和修改。
使用介面定義裝置狀態和操作:
為了進一步提高程式碼的靈活性和可擴充套件性,可以為裝置定義一個統一的介面,用於描述裝置的狀態和操作。例如,可以定義一個DeviceState介面來描述裝置的狀態(如開啟、關閉、調速等),並定義一個DeviceOperation介面來描述對裝置的操作(如切換狀態、調整檔位等)。這樣,不同的裝置類只需要實現這些介面即可與電路系統進行互動,無需關心具體的實現細節。
將裝置即控制裝置都設定介面以更新裝置狀態和調整裝置引數。
可以使用多型代替型別檢查程式中包含多個if-else語句來檢查裝置型別並執行相應的操作。這可以透過Java的多型性來簡化,使得程式碼更加簡潔和易於擴充套件。
五、總結
首先,透過這次的學習和實踐,我深刻體會到了物件導向程式設計(OOP)的魅力和實用性。透過介面和多型性的應用,我能夠理解並應用設計模式,使程式更加靈活和可擴充套件。例如,Circuit類能夠容納任何實現了Device介面的物件,這種設計使得程式具有很高的靈活性和可重用性。
其次,我在實踐中進一步理解了組合與繼承的關係。透過繼承,我可以快速地建立具有相似特性的新裝置類;而透過組合,我可以將不同的裝置組合成複雜的電路結構,實現更高階的功能。這種組合與繼承的設計思想不僅有助於程式碼的複用和組織,而且方便我進行迭代和修改。
此外,我也認識到了程式碼可讀性和可維護性的重要性。透過封裝不同的處理邏輯到單獨的方法中,我可以使程式碼更加清晰和易於理解。同時,使用合適的命名和註釋也能夠提高程式碼的可讀性,使其他開發者能夠更快地理解和修改程式碼。
在未來,我將繼續深入學習Java的異常處理機制,以確保程式的健壯性和穩定性。異常處理是確保程式能夠優雅地處理錯誤情況的關鍵部分,它能夠幫助我更好地識別和解決潛在的問題。
同時,我也計劃學習更多的設計模式,如工廠模式、單例模式、觀察者模式等。這些設計模式是軟體開發中的寶貴財富,它們能夠幫助我最佳化程式碼結構,提高程式的可維護性和可擴充套件性。透過學習和應用這些設計模式,我相信我能夠編寫出更加健壯和高效的程式碼。