【面向对象课程项目:纸牌】Java实例学习(一):优秀源码的分析
在看了上面优秀的源码之后,我开始反思为什么自己的代码 既不易于维护,也不易于加壳.到现在使用JAVAX的时候,自己还是在为那些跳不完的莫名其妙的swing方面的bugs苦恼.(考虑过自己曾经在设计的时候,根本就没有考虑过要加上图形界面的想法,但是这样始终不能算作一个理由--"别看我识字不多!懒和穷永远是一个字!"-).虽然遵循了MVC的开发思路,但是始终没有走出"想到哪儿写到哪儿的思路".这样不仅在效率上会受到严重的影响,而且一旦要在原有的基础上扩展,或让整个程序的代码变得越来越笨拙.最后走向瘫痪.而且实际上除了将函数给封装到类的内部,自己没有做出其他方面 面向对象的 使用,其实质 背离了学院开设 课程的初衷.
这是一个很好的例子,自己在代码中的错误,希望后人不要再犯,前人不要鄙夷;希望能够让闪光点和优秀的习惯得以保留,而那些坏的编码还有不良的习惯会慢慢的减少,消失,从而一点点成长,一点点蜕变.
一.自己模型的内涵
- 先看看自己的类设计:CardSetting类,这个是自己写单机版的黑白棋的时候保留下的一个习惯,将函数的资源和 常量属性给放到一个静态类中.这样的好处是能够集中调试,配置管理,同时也增加了代码的可复用性.但是由于 各个常量没有放在对应被使用的类中,会造成引用时不直观的缺点.源码如下:
package com.Cards.model; /** * @author Rock Lee * */ public class CardSetting { final public static int CARD_COUNT=13; final public static int INDEX_HEART=0;//红桃的角标 ♠ final public static int INDEX_SPADE=1;//黑桃的 角标♥ final public static int INDEX_DIAMOND=2;//方片的角标 ♦ final public static int INDEX_CLUB=3;//草花的角标 ♣ //final public static String[] COLOR={"HEART ♥","SPADE ♠","DIAMOND ♦","CLUB ♣"}; final public static String[] COLOR={"♥","♠","♦","♣"}; final public static String[] NUM={"A","2","3","4","5","6","7","8","9","10","J","Q","K"}; }
可以看到,使用角标来表示,是为了让代码变得易读,但是倘若能够查阅到此源码,上面的几个index就不该要,反而更清晰 - 接下来看基本的Card模型: 属性冗余较多,而且在CardSetting.java中不雅的编码方式影响了构造方法中的阅读性
package com.Cards.model; /** * @author Rock Lee * @version 2012-10-21 16:51:49 * @see CardSetting.java * @proposal Build A Class Contain All the info for ONE Card in the poker * @fix 2012-12-12 13:01:04 * */ public class Card { private String color = null; private String num = null; private boolean visible = false; private boolean red = false; public Card(String color, String num) { this.color = color; this.num = num; // if the color is diamond or heart ,then red is true,else false if (color.equals(CardSetting.COLOR[CardSetting.INDEX_HEART]) || color.equals(CardSetting.COLOR[CardSetting.INDEX_DIAMOND])) this.red = true; else this.red = false; } /* * 若花色,牌面大小相同,则认为是同一张牌 * */ public boolean equals(Object obj) { Card anotherCard = (Card) obj; return this.color.equals((anotherCard.color)) && this.num.equals(anotherCard.num); } public void setVisiable(boolean visible) { this.visible = visible; } public boolean isVisible() { return visible; } public boolean isRed() { return red; } /* * 1.此方法仅用于 命令行,或调试模式中2.重载了 object的使用方法.在整个列,或者deck suit输出的时候,方便 以字符串的方式拼接 */ public String toString() { if (this == null) return "EMPTY"; else { if (this.isVisible()) { return (color + " " + num + " "); } else return "███ "; } } public String getColor() { return color; } /* 以数字的方式,返回牌面的大小 */ public int getNum() { char ch = num.charAt(0); switch (ch) { case 'A': return 1; case 'J': return 11; case 'Q': return 12; case 'K': return 13; default: return Integer.parseInt(num); } } /* 以String的方式,返回牌面的大小 */ public String getNumInString() { return num; } }
- 不得不说,在简化问题的时候,自己仍然没有细看题目要求,为了方便设计,老师将题目中的deck 和discard已经分开画了.
而且这张图也说明了discard只需要展示一张牌,不需要考虑像win纸牌中三张牌的叠加排放.
- 接下来看看自己实现的几个坑爹的数据结构,分别为CardDeck,CardHeap,CardList,CardStack(这个本应该命名为CardSuit的)
- CardHeap类
当时考虑的时候,还在想能不能够把deck和这个heap结合到一个类中,让deck/heap发完牌后,剩下的直接就放在游戏中准备一张张翻开备用,后来因为考虑到了如果要每次展示三张牌到discard,实现的代码量会有些大,所以还是觉得再多封装一个类...package com.Cards.model; import java.util.Vector; /** * @author Rock Lee * @version 2012-10-21 17:04:34 * @proposal Support the initialization for the whole set of poker,and the Cards left on the DECK * */ public class CardHeap { //这里使用了vector来维护一套 "刚刚从扑克牌盒子里取出的 52张牌" private Vector<Card> vector=null; public CardHeap() { this.initialize(); } /*初始化13*4=52张牌*/ public void initialize() { vector=new Vector<Card>(); Card tmp=null; for (int i = 0; i < 13; i++)//13 cards in Color Heart { tmp=new Card(CardSetting.COLOR[CardSetting.INDEX_HEART], CardSetting.NUM[i]); tmp.setVisiable(false); vector.add(tmp); } for (int i = 0; i < 13; i++)//13 cards in Color SPADE { tmp=new Card(CardSetting.COLOR[CardSetting.INDEX_SPADE], CardSetting.NUM[i]); tmp.setVisiable(false); vector.add(tmp); } for (int i = 0; i < 13; i++)//13 cards in Color DIAMOND { tmp=new Card(CardSetting.COLOR[CardSetting.INDEX_DIAMOND], CardSetting.NUM[i]); tmp.setVisiable(false); vector.add(tmp); } for (int i = 0; i < 13; i++)//13 cards in Color CLUB { tmp=new Card(CardSetting.COLOR[CardSetting.INDEX_CLUB], CardSetting.NUM[i]); tmp.setVisiable(false); vector.add(tmp); } } /*分发牌堆里现有的第一张牌*/ public Card giveOutOneCard() { return (vector.remove(0)); } /*洗牌算法,这里参考了 当时张剑的连连看的实现方法 * * 其思想就是for(i=0:size-1) 交换第2张牌和 牌堆里任意一张牌的顺序,循环结束后,牌堆就已经随机排序 * */ public void shuffle() { for (int i = 0; i < vector.size(); i++) { this.swap(i, (int)(Math.random()*vector.size())); } } private void swap(int i,int j) {//支持洗牌shuffle方法 Card tmp=vector.get(i); vector.set(i, vector.get(j)); vector.set(j,tmp); } public boolean isEmpty() { return vector.isEmpty(); } }
- CardHeap类
- 当当当当!愚蠢的设计方法之一终于登台,虽然刚刚开始交的时候还把这个部分当做亮点跟老师胡侃了一番.后来看完了他人 优秀的代码才知道,这样的实现方法只是达到了目的.放下stack使用思路暂且不论,是否还有更好的方法?这在第三篇文章中会再谈到,源码如下:
这里多一句嘴:以后开发除了真的要到外企去工作,或者文档是写给某些特殊要求人群看的,文档不要再用英文写了啊.自己都觉得蛋疼.英文要表达出明白的意思,自己的词汇量又不够,而且生僻的词汇量反倒误了文档或者注释本来的意义.说来好笑,分不清到底哪里应该多用英语,哪里应该尽量使用自己的母语.一个很好的原则:"莫装X,装X遭雷劈" 此外,还有几个问题,当初只是觉得stack比较符合自己在设计过程中的思路,却没有宏观的考虑到 其他 类对其的操作.而且这个机制让人蛋疼的地方就在于,牌必须来回的倒腾,我开始想想用队列吧.后来觉得如果牌只剩下五张了,你两次翻页应该怎么去实现??这个不佳的设置,直接导致了算法的复杂.和代码的不易读...<试着想想更好的实现机制,否则在第三篇文章中,直接将这个 方案给删除..= =|>package com.Cards.model; import java.util.*; /** * @author Rock Lee * @version 2012-10-22 12:54:02 * @see Card.java CardHeap.java * @proposal to support the view of the groups and the way of controlling the * deck * */ public class CardDeck { /* * 该类应该只保留对外 stackShown的展示,而不应该将其他的属性暴露出去.其他的类只能通过访问接口,看到他应该展示的内容 */ public final static int DECK_SIZE = 3; private Vector<Card> vector_previous = null; private Vector<Card> vector_changed = null; private Stack<Card> stackShown = null; /* * 0->表示deck 中开没有开始被取用.可认为是 GUI中deck没有被点击时的状态 1~size()/3 表示显示的现在是哪一套(一套3张) */ private int currentGroupPosition = 0;// remember this doens't start with // zero 0!<0 means // "not started yet",the // index is from 1 to size()/3> private int groupCount = 0;// the number of the groups public CardDeck() { this.initialize(); } private void initialize() { vector_changed = new Vector<Card>(); vector_previous = new Vector<Card>(); stackShown = new Stack<Card>(); } public void addOneCard(Card card) { // receive a card from CardHeap before the game start vector_previous.add(card); } public Stack<Card> getCurrentGroup() { return this.stackShown; } /* * 如果group还没有被初始化,那么就对deck调用startGroup方法,让deck初始化起来 * 如果已被初始化,变换stackShown的内容,加载下一套(3张)牌 并修改对应的指针 * * 倘若到了未被展示的牌堆(vec_pre)的底部(最后了),就重新将vec_changed回倒给vec_pre,开始下一轮的deck循环 */ public Stack<Card> getNextGroup() { /* * if the group hasn't been established yet,you need to initialize the * Groups and return the first one * * else copy the stackShown to vector_changed and move the iterator to * the next group */ if (currentGroupPosition == 0) return this.startGroup(); /* * Judge if this is the end of all the Groups */ else if (currentGroupPosition == groupCount)// this is the last group { vector_changed.addAll(stackShown); stackShown.clear(); vector_previous.clear(); vector_previous.addAll(vector_changed); vector_changed.clear(); return this.startGroup();// reborn } else { vector_changed.addAll(stackShown);// copy the last group to the // changed_vector stackShown.clear(); for (int i = 0; i < DECK_SIZE; i++) { if (!this.loadOneCard()) break; } this.currentGroupPosition++;// begin with 1,not 0 } return stackShown; } /* * 1.计算出第一套(3张)应该被展示的牌,并将currentGroupPosition 这个游标拨到"1"的地方 2.同时返回 * 第一个待被处理的stackShown */ private Stack<Card> startGroup() { /* * group and calculate the number of the group.present the first group */ this.groupCount = (int) (vector_previous.size() / 3); for (int i = 0; i < DECK_SIZE; i++) { if (!this.loadOneCard()) break;// the end of the vec_pre } this.currentGroupPosition = 1; return stackShown; } /* * 如果违背展示的牌堆(vector_pre)已经空了,便返回false 否则加载 第0号牌至stackShown这个对外展示的窗口中,并返回ture */ private boolean loadOneCard() { /* * Load one card from vector_pre to stackShown * * Return true if load successfully * * return false if the previous vector is empty */ if (vector_previous.isEmpty()) return false; Card tmp = this.vector_previous.remove(0); stackShown.add(tmp); return true; } /* * 从stackShown中获取最上方的一张牌,若为空则返回null */ public Card getOneCard() { /* * this might be used to reapOneCard Or to send to one CardList */ if (stackShown.empty()) return null; return stackShown.pop();// might throw Exception } /* 看看stackShown中最上面的一张牌是神马 */ public Card peekOneCard() { return stackShown.peek(); } /* 访问stackShown的对象,能够对其进行一定的操作 */ public Stack<Card> getStackShown() { return this.stackShown; } }
- 相比较之下,学姐的代码很容易就实现了代码的复用,原来继承在实际开发中真的很重要..以前是真的错了,我再也不鄙夷面相对象的开发流程了啊啊啊啊!当我看到自己写的CardList时候,想起当时写代码的时候大段的复制粘贴:这真心是个恶心的习惯,1.大段复制粘贴很容易在细节地方造成纰漏,导致更加严重的问题;2.当可以大段复制的时候,可否从别处考虑代码复用?也许你应该重新考虑你的设计模式了...
package com.Cards.model; import java.util.LinkedList; import java.util.List; /** * @author Rock Lee * @version 2012-10-21 19:38:26 * @aim Encapsulate a List<Card> and offer more interfaces for the GameModel * */ public class CardList { private LinkedList<Card> list = null; public CardList() { this.initialize(); } private void initialize() { list = new LinkedList<Card>(); } public String toString() { /* * To Print the whole card list to the screen,this method would be * called */ String str = ""; for (int i = 0; i < list.size(); i++) { str += list.get(i); } return str; } public void addOneCard(Card card) { /* * this method receive a card and add it to the end of the list */ this.list.add(card); } public Card getOneCard() { /* * The last card is going be polled out and send to one of the 4 stacks */ if (list.isEmpty()) return null; Card result = this.list.pollLast(); this.turnLastCard(); return (result); } public Card peekOneCard() { return this.list.peekLast(); } public void turnLastCard() { /* * If the last card of the list is covered,you need to turn it over */ if (list.isEmpty()) return; if (!list.peekLast().isVisible()) { list.peekLast().setVisiable(true); } } public boolean moveTo(CardList aimCardList) { int breakPoint = this.allowToMove(aimCardList); if (breakPoint == -1) return false; List<Card> sublist = list.subList(breakPoint, list.size()); aimCardList.list.addAll(sublist);// add the content to the aim sublist.clear();// delete the sublist part of the original this.turnLastCard(); return true; } /* * 这个算法有优化的可能,不必把两个卡片的情况都列出来,再一一加以寻找, 更便捷的办法,是先在fromList中,从"牌面大小"上索引到比 * toList的末尾那张牌下一号的牌. * * 接下来再来检验花色相异 和 可见(visible)两个属性 如果 都符合要求,就说明能够搬运. * * 自己的算法底子薄,思考方案的时候一定要问问自己,有没有神马更简洁明了的实现方案(= =| 话说这个方案还是别人的源码启发的囧) */ private int allowToMove(CardList aimCardList) { /* * return the break point of the original list if 2 lists can call the * function moveTo() * * * if it does not match,return -1; */ // the break point is the end of the aimCardList int subListHeadCardNumber = aimCardList.list.peekLast() == null ? 1 : aimCardList.list.peekLast().getNum() - 1; if (subListHeadCardNumber > 13 || subListHeadCardNumber < 1) return -1; int breakPoint = -1; // get 2 possible headCards Card subListCard_0 = null; Card subListCard_1 = null; if (subListHeadCardNumber != 1) {// aimCardList is not empty if (aimCardList.list.peekLast().isRed()) {// then the subhead shall be black subListCard_0 = new Card("♠", CardSetting.NUM[subListHeadCardNumber - 1]); subListCard_1 = new Card("♣", CardSetting.NUM[subListHeadCardNumber - 1]); } else {// then the subhead shall be red subListCard_0 = new Card("♥", CardSetting.NUM[subListHeadCardNumber - 1]); subListCard_1 = new Card("♦", CardSetting.NUM[subListHeadCardNumber - 1]); } breakPoint = this.list.indexOf(subListCard_0); if (breakPoint == -1) breakPoint = this.list.indexOf(subListCard_1); if (breakPoint == -1) return -1; } else // aim card list is empty { for (int i = 0; i < this.list.size(); i++) { if (this.list.get(i).isVisible() && this.list.get(i).getNum() == 13) breakPoint = i; } } return breakPoint; } public boolean allowToAdd(Card card) { Card endCard = list.peekLast(); if (endCard.isRed() == card.isRed())// same red or black return false; return (card.getNum() == endCard.getNum() - 1); } public LinkedList<Card> getLinkedList() { return this.list; } }
- 抛开别的不谈,明白人一看就知道,allowToAdd/allowToMove在本质上就是一个东西,可是自己硬把他们写成了两个方法.
- 那个大段的超过了一屏的allow方法是怎么回事!你都不愿意看到代码,还指望维护和文档?!f**k me.
- moveTo/addOneCard本来也可以整合到一起,自己由于当时什么bug干扰了自己思考的逻辑电路,明显的,没用到重载,也没有想到用ArrayList对象的可变长度解决这个问题...
- 代码一开就写少了,有些有用的set/get也给写了上去..这个真是..
- 放在这个时候,有丰富经验的人知道,应该考虑用继承了(deck/list很多方法是想通的).但是自己知道这样的思路,是在交上代码n周之后...
-
接下来,鸡肋的CardStack.java 其实如果设计的时候,注意到了的话,根本就不用维护一个stack的数据结构,因为只需要获得一个suit的最上面一张牌就行,父类或者一个简单的函数就判断是否能够添加,而又何必引入stack?!一下是苦逼的代码:
package com.Cards.model; import java.util.Stack; /** * @author Rock Lee * @version 2012-10-21 20:35:59 * @aim to support the stack operation of the cards which are abandoned * */ public class CardStack { private Stack<Card> stack=null; private String color=null; public CardStack(String color) { stack=new Stack<Card>(); this.color=color; } public boolean allowToPush(Card card) { /* * is it possible to push the card into the stack * */ if(!card.getColor().equalsIgnoreCase(this.color))//colors match? return false; if(stack.empty()&&(card.getNum()==1))//is the card an ACE? return true; else { if(stack.peek().getNum()+1==card.getNum())//nums match? return true; return false; } } public Card peek() { return this.stack.peek(); } public void push(Card card) { stack.push(card); } public String toString() { if (stack.isEmpty()) return color+" Empty"; else return color+" "+stack.peek().getNumInString(); } public Stack<Card> getStack() { return this.stack; } }
二.模型的整合和协同工作问题:
接下来谈谈GameModel.java这个蛋疼的类.嗯.他实际上几乎什么也没干!!!!本来应该在这个游戏模型里面定义的方法,整合两个数据结构做出 一定的动作,我秀逗的都给放到了comm and这个本应该仅仅负责命令检测的类中了..而且这里有一个很大的隐患,由于没有一个能够进行引用定义的公共父类.我们 的 gameModel就像一个快要散了架的二手老爷车.基本上没有任何辅助功能."油门呢?刹车还有制动呢?擦,火花塞不工作了?" "不不,先生,我想您应该用你的雪茄亲自引燃这台二缸柴油发动机.."
-
是的,正如你看到的,这个类除了初始化一句新游戏他什么也没干..而那几个散了架的数据结构(list stack deck等等)由于没有ArrayList[]这样的而管理而各自为政.要进行操作,还要到整个模型外去自己编写方法,而按照常理,像movePile(from,to)都应该被封装成一个函数,而在control的部分直接调用即可,可是我在这里却什么都没干.如果我是 SVN的同一小组开发的同事,我也会骂一句"去你麻痹的!"package com.Cards.model; /** * @author Rock Lee * @version 2012-10-21 17:33:31 * @proposal to establish a game model for the game,leaving all the possible interfaces to control the game * * */ public class GameModel { public final static int LISTS_NUM=7; public final static int STACKS_NUM=4; public CardHeap heap=null; public CardList lists[]=null; public CardStack stacks[]=null; public CardDeck deck=null; public GameModel() { this.initialize(); } private void initialize() { heap=new CardHeap(); lists=new CardList[LISTS_NUM]; stacks=new CardStack[STACKS_NUM]; } public void newGame() { heap.initialize();//get a new set of cards heap.shuffle(); for (int i = 0; i < lists.length; i++) {//7 times to get 7 Card Lists lists[i]=new CardList(); for (int j = 0; j < i+1; j++) {//i+1 times to give out the enough Cards to build list[i] Card tmp=heap.giveOutOneCard(); lists[i].addOneCard(tmp); } lists[i].turnLastCard();//turn the end card in the list } for (int i = 0; i < stacks.length; i++) {//4 times to get 4 stacks stacks[i]=new CardStack(CardSetting.COLOR[i]); } //only one deck to set up //All the cards left in the heap sent to deck deck=new CardDeck(); while(!heap.isEmpty()) { Card tmp=heap.giveOutOneCard(); tmp.setVisiable(true); deck.addOneCard(tmp); } this.print(); } public void print() { System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); System.out.println("The Status of Deck is :\n\t"+deck.getCurrentGroup()); System.out.println("The Status of 7 lists are:"); for (int i = 0; i < lists.length; i++) { System.out.println("\t"+lists[i]+"\n"); } System.out.println("The Status of 4 stacks are:"); for (int i = 0; i < stacks.length; i++) { System.out.println("\t"+stacks[i]); } System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); } }
-
苦逼的Command.java.他成了GameModel的替身,完成了大部分操作GameModel的逻辑,可是就好像没有放进机箱的组装机器一样.身兼model和control功能的他存在的样子十分丑陋 阿西莫多command
process做了太多本不应该他干的事情.而且由于这种command和model上的耦合,自己无法很好地把控制的部分 重新编写,把 命令行的控制 转移到GUI上面!这才是整个设计中无法原谅的部分!package com.Cards.model; import java.util.Scanner; /** * @author Rock Lee * @version 2012-10-23 10:57:17 * @see Card.java * @proposal To support the command line controls,and the manual of playing the Game * @mail_to if you have any bugs to report ,mail to rocklee.1227@gmail.com * */ public class Command { /* * ============================================================ * This is a manual of playing the game using command line * ============================================================ * OK...I admit that I am too lazy to write a GUI... * There are so much other Course Designs and sucking experiments * to be finished. * * ------------------------------------------------------------ * 1.ltl :move a sublist from list to another e.g.0 6 * 2.rpd :reap from the deck (no arg) * 3.rpl :reap from a list e.g. 6 * 4.dtl :move one card from the deck to a list e.g. 0 * 5.help :help manual (no arg) * 6.pl :print the current status for the game (no arg) * 7.n :turn the next group on the deck (no arg) * 8.exit :exit the whole game (no arg) * 9.new :start a new game (no arg) * ------------------------------------------------------------ * */ final static String[] CMD={"ltl","rpd","rpl","dtl","help","pl","n","exit","new"}; final static String HELP=" * ------------------------------------------------------------\n"+ "* 1.ltl :move a sublist from list to another e.g.0 6\n"+ "* 2.rpd :reap from the deck (no arg)\n"+ "* 3.rpl :reap from a list e.g. 6\n"+ "* 4.dtl :move one card from the deck to a list e.g. 0\n"+ "* 5.help :help to the manul (no arg)\n"+ "* 6.pl :print the current status for the game (no arg)\n"+ "* 7.n :turn the next group on the deck (no arg)\n"+ "* 8.exit :exit the whole game (no arg)\n"+ "* 9.new :start a new game (no arg)\n"+ " * ------------------------------------------------------------\n"+ "* */\n"; Scanner scan_cmd=null; String cmd=null; private int []args={0,0}; private GameModel model=null; public Command(GameModel model) { this.model=model; scan_cmd=new Scanner(System.in); cmd="\0"; } private void getArgument(int index) { /* * Get the arguments for the command just given * */ switch (index) { case 0: args[0]=scan_cmd.nextInt(); args[1]=scan_cmd.nextInt(); break; case 2: case 3: args[0]=scan_cmd.nextInt(); break; default://there is no arguments needed break; } } public void funtion() { /* * make the Command Scanner start to work * */ while(true) { cmd=scan_cmd.next(); int index=this.getCommandIndex(cmd); if(index==-1) { System.out.println("Invalid input command,try it again.\n"); continue; } this.getArgument(index); this.process(index); } } private int getCommandIndex(String cmd) { /* * return the index of the command input * if the input is invalid,return -1 * */ cmd.toLowerCase(); for (int i = 0; i < CMD.length; i++) { if(cmd.equals(CMD[i])) { return (i); } } return -1; } private void process(int index) { /* * The Core Code to do the real job,calling the methods of the fields in GameModel * */ Card tmp=null; switch (index) { case 0://ltl { model.lists[args[0]].moveTo(model.lists[args[1]]); model.print(); break; } case 1://rpd { tmp=model.deck.peekOneCard(); for (int i = 0; i < model.stacks.length; i++) { if(model.stacks[i].allowToPush(tmp)) { model.stacks[i].push(model.deck.getOneCard()); break; } }model.print(); break; } case 2://rpl { tmp=model.lists[args[0]].peekOneCard(); for (int i = 0; i < model.stacks.length; i++) { if(model.stacks[i].allowToPush(tmp)) { model.stacks[i].push(model.lists[args[0]].getOneCard()); break; } } model.print(); break; } case 3://dtl { tmp=model.deck.peekOneCard(); if(model.lists[args[0]].allowToAdd(tmp)) { model.lists[args[0]].addOneCard(model.deck.getOneCard()); }model.print(); break; } case 4://help { System.out.print(Command.HELP); break; } case 5://pl { model.print(); break; } case 6://next group { model.deck.getNextGroup(); model.print(); break; } case 7: { System.exit(0); } case 8: { model.newGame(); break; } default: System.out.println("Command index wrong...."); break; } } }
通过一下午蛋疼的分析,我已经对自己的代码彻底丧失了信心,应该把上面几个天生的残疾修正一下,才能开始编写图形界面.另外自己原本以为会很简单的图形界面,却不如自己想象中的那般容易:到现在,我的牌组整个显示都成问题!!!更不要说担任View和Control的双重任务了.这一块涉及到swing的编程,以后开发类似的桌面应用还会再遇到.一定不要放松对自己的要求,相信对Java的事件驱动图形界面工作流程有了了解,在以后进一步的学习Qt和C# 开发的时候,会更容易一些.最后贴一张运行的示例图,捂脸+叹气:
====================================================================================================
未完待续