伟大软件三步骤:
1.确认你的软件做客户要它做的事
2.运用基本的OO原则来增加软件的灵活性
3.努力实现可维护、可重用的设计。
伟大软件的编写是迭代进行的,先针对整体轮廓操作,接着迭代应用程序的每个片段,直到完成。
有两种方法去迭代深入应用程序的特定部件(part)。必须处理较小的功能片段(piece of functionality)。
可以选择聚焦在应用程序的特定功能(feature)上。此方式完全关系到取出客户想要的一个功能片段,并且实现该功能,直到它完成。
功能驱动开发(Feature Driven Development)挑出应用程序的特定功能,并且规划、分析及开发该功能,直到完成。
也可以选择聚焦在通过应用程序的特定流程(flow)上。此方式取出通过应用程序的完整路径(具有清楚的开始与结束),并且在你的程序代码中实现该路径(path)
用例驱动开发(Use Case Driven Development)挑出通过用例的场景(scenario),并且编写程序代码支持通过该用例的完整场景。
两种迭代(iterating)方式皆由良好的需求(requirement)所驱动。因为需求源自客户,两种方式都是聚焦在交付客户要的东西上。
在使用功能开发时,一次做单一功能(feature);接着迭代,一次解决一个功能,直到你完成应用程序的整个功能性(functionality)。
在用例驱动开发中,你操作通过用例的的单一场景,接着再取出另一个场景并且完成它,直到所有场景被完成。然后再迭代下一个用例,直到所有用力都能运作。
编写测试场景:
测试案例(test case)不必复杂,它们知识提供一个方式,向客户展示类里的功能正常运作。
应该为所有你能想到的可能的使用状况测试你的软件。要有想象力!
也别忘了测试软件不正确使用的状况。你将在早期捕捉住错误,让你的客户高兴。
测试驱动开发聚焦于让类的行为正确。
良好的软件是通过迭代造就而成。分析、设计、再一次迭代,一次一次完成应用程序更小更小的部分。
每当你迭代时,重新评估你的设计决策,假如它对你的设计合理,就别害怕改变。
游戏框架我们增加了Unit UnitTester UnitGroup,项目架构:
Board.java:
1 package headfirst.gsf.board; 2 3 import java.util.ArrayList; 4 import java.util.Iterator; 5 import java.util.List; 6 7 import headfirst.gsf.unit.Unit; 8 9 public class Board { 10 11 private int width, height; 12 private List tiles; 13 14 public Board(int width, int height){ 15 this.width = width; 16 this.height = height; 17 initialize(); 18 } 19 20 private void initialize(){//定义成二维数组的矩形棋盘 21 tiles = new ArrayList(width); 22 for(int i = 0; i < width; i++){ 23 tiles.add(i, new ArrayList(height)); 24 for(int j = 0; j < height; j++){ 25 ((ArrayList) tiles.get(i)).add(j, new Tile()); 26 } 27 } 28 } 29 30 public Tile getTile(int x, int y){ 31 return (Tile) ((ArrayList) tiles.get(x - 1)).get(y - 1); 32 } 33 34 public void addUnit(Unit unit, int x, int y){ 35 Tile tile = getTile(x, y); 36 tile.addUnit(unit); 37 } 38 39 public void removeUnits(int x, int y){ 40 Tile tile = getTile(x, y); 41 tile.removeUnits(); 42 } 43 44 public List getUnits(int x, int y){ 45 return getTile(x, y).getUnits(); 46 } 47 }
Tile.java:
1 package headfirst.gsf.board; 2 3 import java.util.List; 4 import java.util.LinkedList; 5 6 import headfirst.gsf.unit.Unit; 7 8 public class Tile { 9 10 private List units; 11 12 public Tile(){ 13 units = new LinkedList(); 14 } 15 16 protected void addUnit(Unit unit){ 17 units.add(unit); 18 } 19 20 protected void removeUnit(Unit unit){ 21 units.remove(unit); 22 } 23 24 protected void removeUnits(){ 25 } 26 27 protected List getUnits(){ 28 return units; 29 } 30 }
Unit.java:
1 package headfirst.gsf.unit; 2 3 import java.util.HashMap; 4 import java.util.LinkedList; 5 import java.util.List; 6 import java.util.Map; 7 8 public class Unit { 9 private String type; 10 private int id; 11 private String name; 12 private List weapons; 13 private Map properties; 14 15 public Unit(int id){ 16 this.id = id; 17 } 18 19 public int getId(){ 20 return id; 21 } 22 23 //getName() and setName() methods 24 public void setName(String name){ 25 this.name = name; 26 } 27 28 public String getName(){ 29 return name; 30 } 31 32 //getType() and setType() methods 33 public void setType(String type){ 34 this.type = type; 35 } 36 37 public String getType(){ 38 return type; 39 } 40 41 public void addWeapon(List weapon){ 42 if(weapon == null){ 43 weapon = new LinkedList(); 44 } 45 weapons.add(weapon); 46 } 47 48 public List getWeapons(){ 49 return weapons; 50 } 51 52 public void setProperty(String property, Object value){ 53 if(properties == null){ 54 properties = new HashMap(); 55 } 56 properties.put(property, value); 57 } 58 59 public Object getProperty(String property){ 60 if(properties == null){ 61 return null; 62 } 63 64 return properties.get(property); 65 } 66 }
UnitGroup.java:
1 package headfirst.gsf.unit; 2 3 import java.util.HashMap; 4 import java.util.Iterator; 5 import java.util.LinkedList; 6 import java.util.List; 7 import java.util.Map; 8 9 public class UnitGroup { 10 private Map units; 11 12 public UnitGroup(List unitList){ 13 units = new HashMap(); 14 for(Iterator i = unitList.iterator(); i.hasNext();){ 15 Unit unit = (Unit) i.next(); 16 units.put(unit.getId(), unit); 17 } 18 } 19 20 public UnitGroup(){ 21 this(new LinkedList()); 22 } 23 24 public void addUnit(Unit unit){ 25 units.put(unit.getId(), unit); 26 } 27 28 public void removeUnit(int id){ 29 units.remove(id); 30 } 31 32 public void removeUnit(Unit unit){ 33 removeUnit(unit.getId()); 34 } 35 36 public Unit getUnit(int id){ 37 return (Unit) units.get(id); 38 } 39 40 public List getUnits(){ 41 List unitList = new LinkedList(); 42 for(Iterator i = units.entrySet().iterator(); i.hasNext();){ 43 Unit unit = (Unit) i.next(); 44 unitList.add(unit); 45 } 46 47 return unitList; 48 } 49 }
UnitTester.java:
1 package headfirst.gsf.unit; 2 3 public class UnitTester { 4 public void testType(Unit unit, String type, String expectedOutputType){ 5 System.out.println(" Testing setting/getting the type property."); 6 unit.setType(type); 7 String outputType = unit.getType(); 8 if(expectedOutputType.equals(outputType)){ 9 System.out.println("Test passed"); 10 }else{ 11 System.out.println("Test failed: " + outputType + " didn't match " + expectedOutputType); 12 } 13 } 14 15 public void testUnitSpecificProperty(Unit unit, String propertyName, Object inputValue, Object expectedOutputValue){ 16 System.out.println(" Testing setting/getting a unit-specific property."); 17 unit.setProperty(propertyName, inputValue); 18 Object outputValue = unit.getProperty(propertyName); 19 if(expectedOutputValue.equals(outputValue)){ 20 System.out.println("Test passed"); 21 }else{ 22 System.out.println(" Test failed: " + outputValue + " didn't match " + expectedOutputValue); 23 } 24 } 25 26 public void testChangeProperty(Unit unit, String propertyName, Object inputValue, Object expectedOutputValue){ 27 System.out.println(" Testing changing an existing property's value."); 28 unit.setProperty(propertyName, inputValue); 29 Object outputValue = unit.getProperty(propertyName); 30 if(expectedOutputValue.equals(outputValue)){ 31 System.out.println("Test passed"); 32 }else{ 33 System.out.println("Test failed: " + outputValue + " didn't match " + expectedOutputValue); 34 } 35 } 36 37 public void testNonExistentProperty(Unit unit, String propertyName){ 38 System.out.println(" Testing getting a non-existent property's value."); 39 Object outputValue = unit.getProperty(propertyName); 40 if(outputValue == null){ 41 System.out.println("Test passed"); 42 }else{ 43 System.out.println("Test failed with value of " + outputValue); 44 } 45 } 46 47 public static void main(String args[]){ 48 UnitTester tester = new UnitTester(); 49 Unit unit = new Unit(1000); 50 tester.testType(unit, "infantry", "infantry"); 51 tester.testUnitSpecificProperty(unit, "hitPoints", new Integer(25), new Integer(25)); 52 tester.testChangeProperty(unit, "hitPoints", new Integer(15), new Integer(15)); 53 tester.testNonExistentProperty(unit, "strength"); 54 } 55 }
编程契约(programming contract):当你按契约编程(programming by contract)时,你与软件的用户正同意该软件以特定方式行动。
Unit的契约,假使使用它的人是能干的程序设计师,他们能处理返回值为null的情况,因此我们的契约是,当你请求不存在的特性或武器,你能处理null值。
若你不信任客户:
你可能将Unit类中getProperty()方法重写:
1 public Object getProperty(String property) throws IllegalAccessException{ 2 if(properties == null){ 3 throw new IllegalAccessException("What are you doing? No properties"); 4 } 5 Object value = properties.get(property); 6 if(value == null){ 7 throw new IllegalAccessException("You're screwing up! No property value."); 8 }else{ 9 return value; 10 } 11 }
如果他们不信任你:
他们会保护他们的程序代码,并且使用防御性编程:
1 //Some method goes out and gets a unit 2 Unit unit = getUnit(); 3 //Now let's use the unit... 4 String name = unit.getName(); 5 if((name != null) && (name.length() > 0)){ 6 System.out.println("Unit name: " + name); 7 } 8 Object value = unit.getProperty("hitPoints"); 9 if(value != null){ 10 try{ 11 Integer hitPoints = (Integer) value; 12 }catch(ClassCastException e){ 13 //Handle the potential error 14 } 15 }