【面向对象课程项目:纸牌】Java实例学习(二):优秀源码与自己模型的对比

接上一篇文章

【面向对象课程项目:纸牌】Java实例学习(一):优秀源码的分析


在看了上面优秀的源码之后,我开始反思为什么自己的代码 既不易于维护,也不易于加壳.到现在使用JAVAX的时候,自己还是在为那些跳不完的莫名其妙的swing方面的bugs苦恼.(考虑过自己曾经在设计的时候,根本就没有考虑过要加上图形界面的想法,但是这样始终不能算作一个理由--"别看我识字不多!懒和穷永远是一个字!"-).虽然遵循了MVC的开发思路,但是始终没有走出"想到哪儿写到哪儿的思路".这样不仅在效率上会受到严重的影响,而且一旦要在原有的基础上扩展,或让整个程序的代码变得越来越笨拙.最后走向瘫痪.而且实际上除了将函数给封装到类的内部,自己没有做出其他方面 面向对象的 使用,其实质 背离了学院开设 课程的初衷.


这是一个很好的例子,自己在代码中的错误,希望后人不要再犯,前人不要鄙夷;希望能够让闪光点和优秀的习惯得以保留,而那些坏的编码还有不良的习惯会慢慢的减少,消失,从而一点点成长,一点点蜕变.

一.自己模型的内涵

  1. 先看看自己的类设计: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就不该要,反而更清晰
  2. 接下来看基本的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;
    	}
    }
    

  3. 不得不说,在简化问题的时候,自己仍然没有细看题目要求,为了方便设计,老师将题目中的deck 和discard已经分开画了.而且这张图也说明了discard只需要展示一张牌,不需要考虑像win纸牌中三张牌的叠加排放.
  4. 接下来看看自己实现的几个坑爹的数据结构,分别为CardDeck,CardHeap,CardList,CardStack(这个本应该命名为CardSuit的)
    1. CardHeap类
      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 vector=null;
      		
      	public CardHeap()
      	{
      		this.initialize();	
      	}
      	
      	/*初始化13*4=52张牌*/
      	public void initialize()
      	{
      		vector=new Vector();
      		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();
      	}
      }
      
      当时考虑的时候,还在想能不能够把deck和这个heap结合到一个类中,让deck/heap发完牌后,剩下的直接就放在游戏中准备一张张翻开备用,后来因为考虑到了如果要每次展示三张牌到discard,实现的代码量会有些大,所以还是觉得再多封装一个类...

  5. 当当当当!愚蠢的设计方法之一终于登台,虽然刚刚开始交的时候还把这个部分当做亮点跟老师胡侃了一番.后来看完了他人 优秀的代码才知道,这样的实现方法只是达到了目的.放下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 vector_previous = null;
    	private Vector vector_changed = null;
    
    	private Stack 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();
    		vector_previous = new Vector();
    
    		stackShown = new Stack();
    	}
    
    	public void addOneCard(Card card)
    	{
    		// receive a card from CardHeap before the game start
    		vector_previous.add(card);
    	}
    
    	public Stack getCurrentGroup()
    	{
    		return this.stackShown;
    
    	}
    
    	/*
    	 * 如果group还没有被初始化,那么就对deck调用startGroup方法,让deck初始化起来
    	 * 如果已被初始化,变换stackShown的内容,加载下一套(3张)牌 并修改对应的指针
    	 * 
    	 * 倘若到了未被展示的牌堆(vec_pre)的底部(最后了),就重新将vec_changed回倒给vec_pre,开始下一轮的deck循环
    	 */
    	public Stack 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 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 getStackShown()
    	{
    		return this.stackShown;
    
    	}
    
    }
    
    这里多一句嘴:以后开发除了真的要到外企去工作,或者文档是写给某些特殊要求人群看的,文档不要再用英文写了啊.自己都觉得蛋疼.英文要表达出明白的意思,自己的词汇量又不够,而且生僻的词汇量反倒误了文档或者注释本来的意义.说来好笑,分不清到底哪里应该多用英语,哪里应该尽量使用自己的母语.一个很好的原则:"莫装X,装X遭雷劈"  此外,还有几个问题,当初只是觉得stack比较符合自己在设计过程中的思路,却没有宏观的考虑到 其他 类对其的操作.而且这个机制让人蛋疼的地方就在于,牌必须来回的倒腾,我开始想想用队列吧.后来觉得如果牌只剩下五张了,你两次翻页应该怎么去实现??这个不佳的设置,直接导致了算法的复杂.和代码的不易读...<试着想想更好的实现机制,否则在第三篇文章中,直接将这个 方案给删除..=  =|>
  6. 相比较之下,学姐的代码很容易就实现了代码的复用,原来继承在实际开发中真的很重要..以前是真的错了,我再也不鄙夷面相对象的开发流程了啊啊啊啊!当我看到自己写的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 and offer more interfaces for the GameModel
     * */
    public class CardList
    {
    	private LinkedList list = null;
    
    	public CardList()
    	{
    		this.initialize();
    	}
    
    	private void initialize()
    	{
    		list = new LinkedList();
    	}
    
    	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 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 getLinkedList()
    	{
    		return this.list;
    	}
    }
    
    • 抛开别的不谈,明白人一看就知道,allowToAdd/allowToMove在本质上就是一个东西,可是自己硬把他们写成了两个方法.
    • 那个大段的超过了一屏的allow方法是怎么回事!你都不愿意看到代码,还指望维护和文档?!f**k me.
    • moveTo/addOneCard本来也可以整合到一起,自己由于当时什么bug干扰了自己思考的逻辑电路,明显的,没用到重载,也没有想到用ArrayList对象的可变长度解决这个问题...
    • 代码一开就写少了,有些有用的set/get也给写了上去..这个真是..
    • 放在这个时候,有丰富经验的人知道,应该考虑用继承了(deck/list很多方法是想通的).但是自己知道这样的思路,是在交上代码n周之后...
  7. 接下来,鸡肋的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 stack=null;
    	private String color=null;
    
    	public CardStack(String color)
    	{
    		stack=new Stack();
    		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 getStack()
    	{
    		return this.stack;
    		
    	}
    }
    

二.模型的整合和协同工作问题:

接下来谈谈GameModel.java这个蛋疼的类.嗯.他实际上几乎什么也没干!!!!本来应该在这个游戏模型里面定义的方法,整合两个数据结构做出 一定的动作,我秀逗的都给放到了comm and这个本应该仅仅负责命令检测的类中了..而且这里有一个很大的隐患,由于没有一个能够进行引用定义的公共父类.我们 的 gameModel就像一个快要散了架的二手老爷车.基本上没有任何辅助功能."油门呢?刹车还有制动呢?擦,火花塞不工作了?" "不不,先生,我想您应该用你的雪茄亲自引燃这台二缸柴油发动机.." 

  1. 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("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");	
    	}
    
    }
    
    是的,正如你看到的,这个类除了初始化一句新游戏他什么也没干..而那几个散了架的数据结构(list stack deck等等)由于没有ArrayList[]这样的而管理而各自为政.要进行操作,还要到整个模型外去自己编写方法,而按照常理,像movePile(from,to)都应该被封装成一个函数,而在control的部分直接调用即可,可是我在这里却什么都没干.如果我是 SVN的同一小组开发的同事,我也会骂一句"去你麻痹的!"

  2. 苦逼的Command.java.他成了GameModel的替身,完成了大部分操作GameModel的逻辑,可是就好像没有放进机箱的组装机器一样.身兼model和control功能的他存在的样子十分丑陋 阿西莫多command

    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 [email protected]
     * */
    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;
    		}
    	}
    }
    
    process做了太多本不应该他干的事情.而且由于这种command和model上的耦合,自己无法很好地把控制的部分 重新编写,把 命令行的控制 转移到GUI上面!这才是整个设计中无法原谅的部分!

    通过一下午蛋疼的分析,我已经对自己的代码彻底丧失了信心,应该把上面几个天生的残疾修正一下,才能开始编写图形界面.另外自己原本以为会很简单的图形界面,却不如自己想象中的那般容易:到现在,我的牌组整个显示都成问题!!!更不要说担任View和Control的双重任务了.这一块涉及到swing的编程,以后开发类似的桌面应用还会再遇到.一定不要放松对自己的要求,相信对Java的事件驱动图形界面工作流程有了了解,在以后进一步的学习Qt和C# 开发的时候,会更容易一些.

    最后贴一张运行的示例图,捂脸+叹气:

====================================================================================================

未完待续

你可能感兴趣的:(java,心得体会)