寫文本比敲代碼難多了!!!
在我公布了要和“方方土的堃”進(jìn)行項(xiàng)目合作之后,本以為只要老老實(shí)實(shí)按照書本的項(xiàng)目一步一步研發(fā)就可以了,但是沒想到我的合作伙伴把需求改成運(yùn)行在命令行上的系統(tǒng)原型即可,本來我們決定在代碼中不要連數(shù)據(jù)庫,但是等我把幾個(gè)存儲(chǔ)類寫得差不多的時(shí)候呢,方方土的堃 就開始用上 SQL server 了。代碼寫了一半我也不太好改,于是,我打算把剩余部分寫完就結(jié)束該項(xiàng)目了,接下來的日子就好好專心學(xué)習(xí)了。
我的架構(gòu)是按照MVC模式設(shè)計(jì)的,其中 Database 類本來可以直接與數(shù)據(jù)庫相連,然后寫相應(yīng)的業(yè)務(wù)SQL查詢就好了,而我自己手寫了一個(gè),但是個(gè)bug重發(fā)區(qū),曾經(jīng)費(fèi)了不少時(shí)間去調(diào)試。其次,Core類只是對(duì)當(dāng)作程序的入口而用,承擔(dān) Database 與 UI 連接的工作統(tǒng)統(tǒng)寫在 Process 類里, 這個(gè)類負(fù)責(zé)邏輯處理。 UI 類則針對(duì)每一個(gè)用戶可能遇到的每一個(gè)Page都寫了一個(gè)方法,然后交由 Process 調(diào)用,顯示窗口。未來有時(shí)間的話,可以考慮將UI類改寫成 GUI 。
該酒店管理系統(tǒng)現(xiàn)已實(shí)現(xiàn)的功能有:
用戶登陸
用戶權(quán)限控制
前臺(tái)開單
智能結(jié)帳
修改菜單
前后臺(tái)交換下單信息
智能結(jié)帳
生成日匯報(bào)賬單
IO
IO 類只是包括了從命令行獲取輸入的方法和將類序列化與分序列化的方法,其中方法 getInput
、getNum
、getInt
都是通過 Scanner 類來獲取字符串、浮點(diǎn)數(shù)、整數(shù), writeData
、readData
則是通過 ObjectInputStream 和 ObjectOutPutStream 來實(shí)現(xiàn)序列化和反序列化。
IO 類里所有的方法都是 static 的,即類方法。
Database
首先,我先確定好數(shù)據(jù)模型,并設(shè)計(jì)出如下數(shù)據(jù)表:
1. 菜單表(菜名, 價(jià)格, 是否可供應(yīng))
1. 下單表(訂單號(hào), 桌號(hào), 訂單時(shí)間, 訂單金額, 訂單菜名列別, 剩余未上菜列表)
1. 日結(jié)帳表(日期, 金額)
1. 用戶表(員工好, 員工民, 登陸密碼, 權(quán)限角色)
1. 權(quán)限表(權(quán)限角色名, 授權(quán)列表)
把這些表每一行數(shù)據(jù)項(xiàng)用一個(gè)類去寫,然后再以各表主碼為key,其類實(shí)例為value的hashmp作為 Database 的屬性,通過這種方式將不同數(shù)據(jù)聚合于 Database 類。比如說以菜單表為例子, Database 的結(jié)構(gòu)將會(huì)像這樣子的。
public class Database implements Serializable { ? ?private Hashmap<String, Dish> dishTable = new ?Hashmap<String, Dish>();//菜名是字符串
? ?//...其他表
? ?//...具體業(yè)務(wù)方法}class Dish implements Serializable { ? ?float price; ? ?boolean isAvailable;
}
然后 Database 的方法與具體某項(xiàng)業(yè)務(wù)有關(guān),為了省時(shí)間,則不做抽象,每一個(gè)方法實(shí)現(xiàn)業(yè)務(wù)的目標(biāo)就好了。
為了持久化, Database 及數(shù)據(jù)項(xiàng)類都應(yīng)用了 Serializable 接口。而 Database 的序列化和反序列化將由 Process 類調(diào)用 IO 方法執(zhí)行。
Core/Process
首先, Core 類只是對(duì)程序的初始化而已,具體代碼像這樣:
public class Core { ? ?public static void main(String[] args) {
? ? ? ?Process app = new Process();
? ? ? ?UI ui = new UI();
? ? ? ?IO io = new IO();//初始化以免發(fā)生某些錯(cuò)誤
? ? ? ?app.start();
? ?}
}
Process 就是程序主體,在程序一開始,則需要加載判斷數(shù)據(jù)庫文件是否存在,以決定是執(zhí)行登陸還是重新注冊(cè)。于是 start 方法可以這么寫:
class Process {
? ?Database database; ? ?//...其他類成員變量
? ?public void exit() { ? ? ? ? ? ? ? ?//程序結(jié)束時(shí)保存數(shù)據(jù)庫
? ? ? ?IO.writeData(database, fileDir);
? ? ? ?System.exit(0);
? ?} ? ?public void start() {
? ? ? ?UI.welcomePage(); ? ? ? ? ? ? ? //歡迎頁
? ? ? ?if (IO.isFileExists(fileDir)) { //判斷指定路徑的數(shù)據(jù)庫文件是否存在
? ? ? ? ? ?database = (Database) IO.readData(fileDir);
? ? ? ? ? ?login();
? ? ? ?} else {
? ? ? ? ? ?DBAregister(); ? ? ? ? ? ? ?//重新安裝,注冊(cè)管理員
? ? ? ?}
? ? ? ?on(); ? ? ? ? ? ? ? ? ? ? ? ? ? //進(jìn)入菜單,選擇具體動(dòng)作
? ?}
}
Process 還實(shí)現(xiàn)了權(quán)限控制。首先應(yīng)該知道 Process.on
的結(jié)構(gòu)是這樣子的:
class Process { ? ?public void on() { ? ? ? ?while(true) { ? ? ? ? ? ?int choice = ?UI.mainMenuPage(rolePermission); ?//UI展示菜單頁
? ? ? ? ? ?switch (choice) { ? ? ? ? ? ? ? ?case 0:
? ? ? ? ? ? ? ? ? ?exit();break; ? ? ? ? ? ? ? ?//...case n 分別對(duì)應(yīng)一次具體業(yè)務(wù)動(dòng)作
? ? ? ? ? ? ? ?default: ? ? ? ? ? ? ? ? ? ?break;
? ? ? ? ? ?}
? ? ? ?}
? ?}
}
其中關(guān)鍵是數(shù)組 rolePermission
,具體思路是用戶在登錄時(shí)會(huì)得到自己的權(quán)限角色,然后通過查詢數(shù)據(jù)庫的權(quán)限表,將獲得的許可數(shù)組賦值到 ArrayList<Integer> Process.rolePermission
,在 UI 菜單展示頁則會(huì)根據(jù) rolePermission 里涉及的選項(xiàng)讓用戶選擇 rolePermission 的 index, 然后 return rolePermission.get(index);
便實(shí)現(xiàn)權(quán)限的控制了。【即你能選到的只可能是 rolePermission 里對(duì)應(yīng)的case】
UI
UI 類的設(shè)計(jì)反而是最簡(jiǎn)單的,因?yàn)樵谶@個(gè)類你只需要考慮你想看到什么,然后處理用戶輸入的步驟以及反饋就好了,所以可以先寫一些小的組件比如說 titleShow()
, inputTip()
等等,再對(duì)應(yīng)與 Process 的具體業(yè)務(wù)組合成一個(gè)個(gè) xxxxxPage()
,比如說 welcomePage
、mainMenuPage
等。 UI 類所有方法也都是 static 修飾的。
有一點(diǎn)想要提一下的是,在我編程編到后面的時(shí)候,突然覺得 Database 方法以及 UI方法 的接口并不是一致的,然后我相當(dāng)多的數(shù)據(jù)轉(zhuǎn)換都在 Process 里進(jìn)行, 再加上出現(xiàn)類似新添菜式是,需要 UI.addDishPage()
返回一個(gè) String dishName
和 float price
,而java并不像Python那樣支持多返回值,因此我專門設(shè)計(jì)了一個(gè)類 Data ,它是用于 Database 和 UI 返回多個(gè)數(shù)據(jù)值使用,每次返回的時(shí)候, new Data()
然后向相應(yīng)的成員進(jìn)行賦值。
class Data { ? ?int intNum; ? ?float floatNum; ? ?//...其他出現(xiàn)的數(shù)據(jù)類型}
上面就是我對(duì)此次項(xiàng)目的一個(gè)總結(jié),如果大家有什么看法,歡迎通過公眾號(hào)或者github提交issue的方式進(jìn)行交流。
項(xiàng)目的github地址: https://github.com/SunKingSuper/myProject/tree/master/hsm