王大虎

王大虎:20.1 整理项目信息——苦差事

王大虎

周五下午,我正在看技术网站,第六感官发觉有人在身后,扭头一看,老大站在背后,我赶忙站起来。

“王经理,你找我?”

“哦,在看技术呀。有个事情找你谈一下,你到我办公室来一下。”

到老大办公室还没坐稳,老大就开始发话了。

“是这样,刚刚我在看季报,我们每个项目的支出费用都很高,项目情况复杂,人员情况也不简单,我看着也有点糊涂,你看,这是我们现在还在开发或者维护的103个项目,项目信息很乱,很多是两年前的信息,你能不能先把这些项目最新情况重新打印一份给我,咱们好查查到底有什么问题。”老大说。

“这个好办,我马上去办!”X快地答复道。

很快我设计了一个类图,准备实施,如图20-1所示。

 

图20-1 项目信息类图

简单得不能再简单的类图,是个程序员都能实现。我们来看看这个简单的东西,先看接口,如代码清单20-1所示。

代码清单20-1 项目信息接口

 

* * *

 

public interface IProject { //从老板这里看到的就是项目信息 public String getProjectInfo(); }

 

王大虎

 

定义了一个接口,面向接口编程嘛,当然要定义接口了,然后看看实现类,如代码清单20-2所示。

代码清单20-2 项目信息的实现

 

* * *

 

public class Project implements IProject { //项目名称 private String name = “”; //项目成员数量 private int num = 0; //项目费用 private int cost = 0; //定义一个构造函数,把所有老板需要看到的信息存储起来 public Project(String name,int num,int cost){ //赋值到类的成员变量中 this.name = name; this.num = num; this.cost=cost; } //得到项目的信息 public String getProjectInfo() { String info = “”; //获得项目的名称 info = info+ “项目名称是:” + this.name; //获得项目人数 info = info + “\t项目人数: “+ this.num; //项目费用 info = info+ “\t 项目费用:”+ this.cost; return info; } }

 

* * *

 

实现类也是极度简单,通过构造函数把要显示的数据传递过来,然后放到getProjectInfo中显示,这太容易了!然后我们老大要看看结果了,如代码清单20-3所示。

代码清单20-3 老大看报表的场景

 

王大虎

 

public class Boss { public static void main(String[] args) { //定义一个List,存放所有的项目对象 ArrayList<IProject> projectList = new ArrayList<IProject>(); //增加星球大战项目 projectList.add(new Project(“星球大战项目”,10,100000)); //增加扭转时空项目 projectList.add(new Project(“扭转时空项目”,100,10000000)); //增加超人改造项目 projectList.add(new Project(“超人改造项目”,10000,1000000000)); //这边100个项目 for(int i=4;i<104;i++){ projectList.add(new Project(“第”+i+”个项目”,i*5,i*1000000)); } //遍历一下ArrayList,把所有的数据都取出 for(IProject project:projectList){ System.out.println(project.getProjectInfo()); } } }

 

public class Boss { public static void main(String[] args) { //定义一个List,存放所有的项目对象 IProject project = new Project(); //增加星球大战项目 project.add(“星球大战项目ddddd”,10,100000); //增加扭转时空项目 project.add(“扭转时空项目”,100,10000000); //增加超人改造项目 project.add(“超人改造项目”,10000,1000000000); //这边100个项目 for(int i=4;i<104;i++){ project.add(“第”+i+”个项目”,i*5,i*1000000); } //遍历一下ArrayList,把所有的数据都取出 IProjectIterator projectIterator = project.iterator(); while(projectIterator.hasNext()){ IProject p = (IProject)projectIterator.next(); System.out.println(p.getProjectInfo()); } } }

 

王大虎
王大虎

运行结果如下所示:

项目名称是:星球大战项目 项目人数: 10 项目费用:100000 项目名称是:扭转时空项目 项目人数: 100 项目费用:10000000 项目名称是:超人改造项目 项目人数: 10000 项目费用:1000000000 项目名称是:第4个项目 项目人数: 20 项目费用:4000000 项目名称是:第5个项目 项目人数: 25 项目费用:5000000 . . .

 

* * *

 

运行结果完全相同,但是上面的程序复杂性增加了不少,难道我们退回到原始时代了吗?非也,非也,只是我们回退到JDK 1.0.8版本的编程时代了,我们使用一种新的设计模式——迭代器模式。

王大虎

20.4 最佳实践

如果你是做Java开发,尽量不要自己写迭代器模式!省省吧,使用Java提供的Iterator一般就能满足你的要求了。

第21章 组合模式

 

这个程序比较长,如果在我们的项目中有这样的程序,肯定是要被拉出来做典型的,你写一大坨的程序给谁呀,以后还要维护,程序要短小精悍!X的是,我们这是作为案例来讲解,而且就是指出这样组装这棵树是有问题的,等会我们深入讲解,先看运行结果:

 

* * *

 

名称:王X子 职位:总经理     薪水: 100000 名称:刘大瘸子 职位:研发部门经理 薪水:10000 名称:杨三乜斜 职位:开发一组组长 薪水:5000 名称:a 职位:开发人员 薪水:2000 名称:b 职位:开发人员 薪水:2000 名称:c 职位:开发人员 薪水:2000 名称:吴大棒槌 职位:开发二组组长 薪水:6000 名称:d 职位:开发人员 薪水:2000 名称:e 职位:开发人员 薪水:2000 名称:f 职位:开发人员 薪水:2000 名称:郑老六 职位:研发部副总 薪水:20000 名称:马二拐子 职位:X部门经理 薪水:20000 名称:h 职位:X人员 薪水:5000 名称:i 职位:X人员 薪水:4000 名称:赵三驼子 职位:财务部经理 薪水:30000 名称:j 职位:财务人员 薪水:5000 名称:k 职位:CEO秘书 薪水:8000

 

王大虎

 

和我们期望的结果一样,一棵完整的树就生成了,而且我们还能够遍历。不错,不错,但是看类图或程序的时候,你有没有发觉有问题?getInfo每个接口都有,为什么不能抽象出来?Root类和Branch类有什么差别?根节点本身就是树枝节点的一种,为什么要定义成两个接口两个类?如果我要加一个任职期限,你是不是每个类都需要修改?如果我要后序遍历(从员工找到他的上级领导)能做到吗?——彻底晕菜了!

问题很多,我们一个一个解决,先说抽象的问题。我们确实可以把IBranch和IRoot合并成一个接口,确认无疑的事我们先做,那我们就修改一下类图,如图21-3所示。

仔细看看这个类图,还能不能发现点问题。想想看接口的作用是什么?定义一类事物所具有的共性,那ILeaf和IBranch是不是也有共性呢?有,getInfo方法!我们是不是要把这个共性也封装起来呢?是的,是的,提炼事物的共同点,然后封装之,这是我们作为设计专家的拿手好戏,修改后的类图如图21-4所示。

 

图21-3 整合根节点和树枝节点后的类图

 

 

王大虎

 

 

public abstract class Corp { //公司每个人都有名称 private String name = “”; //公司每个人都职位 private String position = “”; //公司每个人都有薪水 private int salary =0; public Corp(String _name,String _position,int _salary){ this.name = _name; this.position = _position; this.salary = _salary; } //获得员工信息 public String getInfo(){ String info = “”; info = “姓名:” + this.name; info = info + “\t职位:”+ this.position; info = info + “\t薪水:” + this.salary; return info; } }

 

抽象类嘛,就应该抽象出一些共性的东西出来,然后看两个具体的实现类,树叶节点如代码清单21-15所示。

代码清单21-15 树叶节点

 

* * *

 

public class Leaf extends Corp { //就写一个构造函数,这个是必须的 public Leaf(String _name,String _position,int _salary){ super(_name,_position,_salary); } }

 

* * *

 

这个精简得比较多,几行代码就完成了,确实就应该这样,下面是小头目的实现类,如代码清单21-16所示。

代码清单21-16 树枝节点

 

王大虎

 

 

public class Branch extends Corp { //领导下边有那些下级领导和小兵 ArrayList<Corp> subordinateList = new ArrayList<Corp>(); //构造函数是必须的 public Branch(String _name,String _position,int _salary){ super(_name,_position,_salary); } //增加一个下属,可能是小头目,也可能是个小兵 public void addSubordinate(Corp corp) { this.subordinateList.add(corp); } //我有哪些下属 public ArrayList<Corp> getSubordinate() { return this.subordinateList; } }

 

* * *

 

场景类中构建树形结构,并进行遍历。组装没有变化,遍历组织机构数稍有变化,如代码清单21-17所示。

代码清单21-17 稍稍修改的场景类

 

* * *

 

public class Client { //遍历整棵树,只要给我根节点,我就能遍历出所有的节点 public static String getTreeInfo(Branch root){ ArrayList<Corp> subordinateList = root.getSubordinate(); String info = “”; for(Corp s :subordinateList){ if(s instanceof Leaf){ //是员工就直接获得信息 info = info + s.getInfo()+”\n”; }else{ //是个小头目 info = info+s.getInfo()+”\n”+ getTreeInfo((Branch)s); } } return info; } }

 

* * *

 

场景类中main方法没有变动,请参考代码清单21-7所示,不再赘述。遍历组织机构树的getTreeInfo稍有修改,就是把用到ICorp接口的地方修改为Corp抽象类,仅仅修改了粗体部分,其他保持不变,运行结果相同。这就是组合模式。

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注