文章转自:http://www.cnblogs.com/iou123lg/archive/2013/03/24/2977945.html
最近在自学设计,碰到一题目,我相信网上很多地方也能找到这个题目,题目内容如下:
农场一头小母牛
每年生头小母牛
母牛五岁产母牛
二十年上多少牛
请使用OO思想来解决这个问题。
这题目很有意思,为什么呢?因为读起来朗朗上口,很顺溜,哈哈,开个玩笑。
OK,下面我将结合所学的知识和技巧来描述下自己解决这个问题的过程。
名词
从题目内容中,我们可以抽取出多个名词,如农场、母牛等,为什么我们要把名词抽取出来?因为在OO思想中,就是要有类或者对象,而抽取出来的名词就有可能是我们需要的类或者对象,当然也有可能是类的属性,这个我们就需要结合具体的情况来分析。通过名词抽取,我确定我需要两个类,分别是农场和母牛,我命名为Farm和Cow。
/**
*
* @author LingJian
*
*/
public class Farm {
}
/**
*
* @author LingJian
*
*/
public class Cow {
}
属性
确定了类,接下来就需要确定类的属性。有些时候我们可以从给的材料或者需求中直接就定义到相关类的属性,但是在这个题目里面我们是需要慢慢的理解隐含的关系才能确定的。首先由“母牛五岁产母牛”可以确定的属性是母牛的年龄(age),当然在这个地方是不是还有一个生育年龄呢?还有是不是有一个生育数呢?而农场呢,咋一看,貌似只有牛,那么它的属性是牛(Cow),还是牛群(List<Cow>)呢?在我的设计里面,牛是有年龄、生育年龄、生育数三个属性的,只不过我把生育年龄和生育数定义为常量,而农场则有牛群和经营时间两个属性。
/**
*
* @author LingJian
*
*/
public class Farm {
//农场经营时间
private int year;
//牛群
private List<Cow> cows;
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public List<Cow> getCows() {
return cows;
}
public void setCows(List<Cow> cows) {
this.cows = cows;
}
}
/**
*
* @author LingJian
*
*/
public class Cow {
//生育年龄
private static final int BEARAGE = 5;
//生育数
private static final int BORNCOUNT = 1;
//年龄
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
类之间的关系、方法
有了类,有了属性,那么接下来我们就要去确定这些个类的方法,而类的方法往往都是由类之间的关系确定的,举个例子,工厂模式中Factory的create方法,假如工厂生产玩具,正是因为工厂(Factory)会生产玩具(Toy),有这样一层关系,所以Factory会有一个create方法,这个方法就返回Toy的实例。在我们的题目环境中,Farm与Cow,随着经营时间变化,农场的牛不断长大,长大到了5岁会生出小牛,这些小牛又不断长大,而农场的牛群就因此在壮大,所以牛会有一个长大(growUp)方法,农场会有一个随时间变化的方法,我把它命名为past。
隐藏(封装)
在确定了方法之后,我们很自然的就会去想方法体里面该写些什么?很多时候,我们会对方法的细节特别关注,牛到了五岁时,生出来的牛是不是应该分一下是最先的母牛生的,还是后面生出来的母牛生的。至少当时我就一直在抠这个细节,而后来我发现面向对象的一大特性就是封装,隐藏一些无须关注细节,因为我们不是在计算二十年后,最先的母牛生了多少崽,而是关注农场二十年后有多少牛,当然如果问题是问你二十年后生了崽的母牛都有多少崽或孙崽等或许就需要抠这个细节了,封装的好处就是降低耦合,避免方法不得不需要修改时而造成的牵一发而动全身,增强可扩展性。
到此,OO思想解决这个问题就基本完成了,最后写一个main方法来测试。
/**
*
* @author LingJian
*
*/
public class Farm {
//农场经营时间
private int year;
//牛群
private List<Cow> cows;
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public List<Cow> getCows() {
return cows;
}
public void setCows(List<Cow> cows) {
this.cows = cows;
}
public void past(int time) {
while(this.year < time) {
this.year ++;
int j = cows.size();
for(int i=0; i<j; i++) {
System.out.println(i);
Cow c = cows.get(i);
c.growUp(this);
}
System.out.println("第" + year + "年,农场有" + cows.size() + "头牛");
}
}
}
/**
*
* @author LingJian
*
*/
public class Cow {
//生育年龄
private static final int BEARAGE = 5;
//生育数
private static final int BORNCOUNT = 1;
//年龄
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Cow(int age) {
super();
this.age = age;
}
public void growUp(Farm f) {
this.age ++;
//母牛五岁生母牛
if(age >= BEARAGE) {
//每年生头小母牛
for(int i=0; i<BORNCOUNT; i++) {
f.getCows().add(new Cow(0));
}
}
}
}
/**
* 测试类
* @author LingJian
*
*/
public class Test {
/**
* 测试方法
* @param args
*/
public static void main(String[] args) {
List<Cow> cows = new ArrayList<Cow>();
Farm f = new Farm();
//农场一头小母牛
cows.add(new Cow(0));
f.setCows(cows);
//二十年上多少牛
f.past(20);
}
}
继承和多态
若干年后,农场主发现养牛不挣钱啦,而且小牛要等到5岁的时候才生小牛,每次才生1头,这样生产力和生产效率都不行啊,这个时候他想养猪,养猪挣钱呐,繁殖能力也强,于是咱们又写了个Pig类,然后把main方法改改,又过了一段时间,农场主嫌养猪也烦,想养鸡养鸭,然后就会发现我们不停的改main方法,在很多项目中就是我们不停的根据需求的变动改以前的写好的代码,而且做得都是牵一发动全身的事情,但是如果我们能写出可扩展性非常不错的设计,或许客户的需求一变动,我们只要改一个地方就完事了那该多爽,OK,回到刚刚养猪的问题,这个地方我们如果使用OO的继承(实现)和多态的话,即可在main方法做小小的改动,就可计算出结果。最后说一个有关生育年龄和生育数的设计,这里我都是在类的属性里面赋以常量,如果我们要提高扩展性,可增加一个配置文件,通过反射取得配置文件中的key和value,这样每次不管是养牛也好,养猪也好,生育年龄和生育数改变只需在main方法和配置文件中稍作变动,就可以得到驯养不同动物的结果。
/**
* 可驯养的动物接口
* 关于抽象类和接口
* 脑子里有这个概念,但是没有具体的东西可以设计为抽象类 如交通工具
* 一类或几类事物的共同特征,如可驯养的 就设计为接口
* @author LingJian
*
*/
public interface Tamableness {
public void growUp(Farm f);
}
/**
*
* @author LingJian
*
*/
public class Farm {
//农场经营时间
private int year;
//可驯养动物
private List<Tamableness> t;
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public List<Tamableness> getT() {
return t;
}
public void setT(List<Tamableness> t) {
this.t = t;
}
public void past(int time) {
while(this.year < time) {
this.year ++;
int j = t.size();
for(int i=0; i<j; i++) {
System.out.println(i);
Tamableness c = t.get(i);
c.growUp(this);
}
System.out.println("第" + year + "年,农场有" + t.size() + "头可驯养的能挣钱的动物");
}
}
}
/**
* 不养牛
* @author LingJian
*
*/
public class Cow implements Tamableness {
//生育年龄
private static final int BEARAGE = 5;
//生育数
private static final int BORNCOUNT = 1;
//年龄
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Cow(int age) {
super();
this.age = age;
}
@Override
public void growUp(Farm f) {
this.age ++;
//母牛五岁生母牛
if(age >= BEARAGE) {
//每年生头小母牛
for(int i=0; i<BORNCOUNT; i++) {
f.getT().add(new Cow(0));
}
}
}
}
/**
* 改养猪
* @author LingJian
*
*/
public class Pig implements Tamableness {
//生育年龄
private static final int BEARAGE = 8;
//生育数
private static final int BORNCOUNT = 8;
//年龄
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Pig(int age) {
super();
this.age = age;
}
@Override
public void growUp(Farm f) {
this.age ++;
if(age >= BEARAGE) {
for(int i=0; i<BORNCOUNT; i++) {
f.getT().add(new Pig(0));
}
}
}
}
/**
* 测试类
* @author LingJian
*
*/
public class Test {
/**
* 测试方法
* @param args
*/
public static void main(String[] args) {
List<Tamableness> list = new ArrayList<Tamableness>();
Farm f = new Farm();
//养什么 就new什么
list.add(new Pig(0));
f.setT(list);
f.past(20);
}
}