29.1 我有一个梦想……
我们每个人都有理想,但不要只是空想,理想是要靠今天的拼搏来实现的。今天咱们就来谈谈自己的理想,如希望成为一个富翁,身价过亿,有两家大公司,一家是房地产公司, 另一家是服装制造公司。这两家公司都很赚钱,天天帮你累积财富。其实你并不关心公司的类型,你关心的是它们是不是在赚钱,赚了多少,这才是你关注的。商人嘛,唯利是图是其本性,偷税漏税是方法,欺上瞒下、压榨员工血汗是常用的手段,先用类图表示一下这两个公司,如图29-1所示。
图29-1 盈利模式的类图
类图很简单,声明了一个Corp抽象类,定义一个公司的抽象模型,公司首要是赚钱的,做义务或善举那也是有背后利益支撑的,还是赞成这句话“天下熙熙,皆为利来;天下攘攘,皆为利往”。我们先看Corp类的源代码,如代码清单29-1所示。
代码清单29-1 抽象公司
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public abstract class Corp {
protected abstract void produce(); protected abstract void sell(); public void makeMoney(){ this.produce(); this.sell(); } }
|
怎么这是模板方法模式啊?是的,这是个引子,请继续往下看。合适的方法存在合适的类中,这个基本上是每本Java基础书上都会讲的,但是到实际的项目中应用的时候就不是这么回事儿了。我们继续看两个实现类是如何实现的,先看HouseCorp类,这是最赚钱的公司,如代码清单29-2所示。
代码清单29-2 房地产公司
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class HouseCorp extends Corp { protected void produce() { System.out.println("房地产公司盖房子..."); } protected void sell() { System.out.println("房地产公司出售房子..."); } public void makeMoney(){ super.makeMoney(); System.out.println("房地产公司赚大钱了..."); } }
|
房地产公司按照正规翻译来说应该是realty corp,这个是比较准确的翻译,但是我问你把房地产公司翻译成英文,你的第一反应是什么?house corp!这是中式英语。我们再来看服装公司,虽然不景气,但好歹也是赚钱的,如代码清单29-3所示。
代码清单29-3 服装公司
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class ClothesCorp extends Corp { protected void produce() { System.out.println("服装公司生产衣服..."); } protected void sell() { System.out.println("服装公司出售衣服..."); } public void makeMoney(){ super.makeMoney(); System.out.println("服装公司赚小钱..."); } }
|
两个公司都有了,那肯定有人会关心两个公司的运营情况。你也要知道它是生产什么的,以及赚多少钱吧。通过场景类来进行模拟,如代码清单29-4所示。
代码清单29-4 场景类
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Client { public static void main(String[] args) { System.out.println("-------房地产公司是这样运行的-------"); HouseCorp houseCorp =new HouseCorp(); houseCorp.makeMoney(); System.out.println("\n"); System.out.println("-------服装公司是这样运行的-------"); ClothesCorp clothesCorp = new ClothesCorp(); clothesCorp.makeMoney(); } }
|
这段代码很简单,运行结果如下所示:
1 2 3 4 5 6 7 8
| -------房地产公司是这样运行的------- 房地产公司盖房子... 房地产公司出售房子... 房地产公司赚大钱了... -------服装公司是这样运行的------- 服装公司生产衣服... 服装公司出售衣服... 服装公司赚小钱...
|
上述代码完全可以描述我现在的公司,但是你要知道万物都是运动的,你要用运动的眼光看问题,公司才会发展……终于有一天你觉得赚钱速度太慢,于是你上下疏通,左右打关系,终于开辟了一条赚钱的“康庄大道”:生产山寨产品!什么产品呢?即市场上什么牌子的东西火爆我生产什么牌子的东西,不管是打火机还是电脑,只要它火爆,我就生产,赚过了高峰期就换个产品,打一枪换一个牌子,不承担售后成本、也不担心销路问题,我只要正品的十分之一的价格,你买不买?哈哈,赚钱啊!
企业的方向定下来了,通过调查,苹果公司的iPod系列产品比较火爆,那咱就生产这个,把服装厂改成iPod生产厂,看类图的变化,如图29-2所示。
图29-2 服装公司改头换面后的类图
好,我的企业改头换面了,开始生产iPod产品了,看我IPodCorp类的实现,如代码清单29-5所示。
代码清单29-5 iPod山寨公司
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class IPodCorp extends Corp { protected void produce() { System.out.println("我生产iPod..."); } protected void sell() { System.out.println("iPod畅销..."); } public void makeMoney(){ super.makeMoney(); System.out.println("我赚钱呀..."); } }
|
服装工厂改成了电子工厂,你这个董事长还是要去看看到底生产什么的,场景类如代码清单29-6所示。
代码清单29-6 场景类
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Client { public static void main(String[] args) { System.out.println("-------房地产公司是按这样运行的-------"); HouseCorp houseCorp =new HouseCorp(); houseCorp.makeMoney(); System.out.println("\n"); System.out.println("-------山寨公司是按这样运行的-------"); IPodCorp iPodCorp = new IPodCorp(); iPodCorp.makeMoney(); } }
|
确实,只用修改了黑色字体这几句话,服装厂就开始变成山寨iPod生产车间,然后你就看着你的财富在积累。山寨的东西不需要特别的销售渠道(正品到哪里我就到哪里),不需要维修成本(大不了给你换个,你还想怎么样,过了高峰期我就改头换面了,你找谁维修去?投诉?投诉谁呢?),不承担广告成本(正品在打广告,我还需要吗?需要吗?),但是也有犯愁的时候,这是一个山寨工厂,要及时地生产出市场上流行的产品,转型要快,要灵活,今天从生产iPod转为生产MP4,明天再转为生产上网本,这都需要灵活的变化,不要限制得太死!那问题来了,每次我的厂房,我的工人,我的设备都在,不可能每次我换个山寨产品厂子就彻底不要了。这不行,成本忒高了点,那怎么办?
Thinking,Thinking…I got an idea!(跳跳虎语),既然产品和工厂绑得太死,那我就给你来 松松,改变设计,如图29-3所示。
图29-3 使用快速变化的类图
公司和产品之间建立关联关系,可以彻底解决以后山寨公司生产产品的问题,工厂想换产品?太容易了!看程序说话,先看Product抽象类,如代码清单29-7所示。
代码清单29-7 抽象产品类
1 2 3 4 5 6
| public abstract class Product { public abstract void beProducted(); public abstract void beSelled(); }
|
简单!忒简单了!House产品类如代码清单29-8所示。
代码清单29-8 房子
1 2 3 4 5 6 7 8 9 10
| public class House extends Product { public void beProducted() { System.out.println("生产出的房子是这样的..."); } public void beSelled() { System.out.println("生产出的房子卖出去了..."); } }
|
既然是产品类,那肯定有两种行为要存在:被生产和被销售,否则就不能称为产品了。我们再来看iPod产品类,如代码清单29-9所示。
代码清单29-9 iPod产品
1 2 3 4 5 6 7 8
| public class IPod extends Product { public void beProducted() { System.out.println("生产出的iPod是这样的..."); } public void beSelled() { System.out.println("生产出的iPod卖出去了..."); } }
|
产品是由公司生产出来的,我们来看公司Corp抽象类,如代码清单29-10所示。
代码清单29-10 抽象公司类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public abstract class Corp { private Product product; public Corp(Product product){ this.product = product; } public void makeMoney(){ this.product.beProducted(); this.product.beSelled(); } }
|
这里多了个有参构造,其目的是要继承的子类都必选重写自己的有参构造函数,把产品类传递进来,再看子类HouseCorp的实现,如代码清单29-11所示。
代码清单29-11 房地产公司
1 2 3 4 5 6 7 8 9 10 11
| public class HouseCorp extends Corp { public HouseCorp(House house){ super(house); } public void makeMoney(){ super.makeMoney(); System.out.println("房地产公司赚大钱了..."); } }
|
理解上没有多少难度,不多说,继续看山寨公司的实现,如代码清单29-12所示。
代码清单29-12 山寨公司
1 2 3 4 5 6 7 8 9 10 11
| public class ShanZhaiCorp extends Corp { public ShanZhaiCorp(Product product){ super(product); } public void makeMoney(){ super.makeMoney(); System.out.println("我赚钱呀..."); } }
|
HouseCorp类和ShanZhaiCorp类的区别是在有参构造的参数类型上,HouseCorp类比较明 确,我就是只要House类,所以直接定义传递进来的必须是House类, 一个类尽可能少地承 担职责,那方法也一样,既然HouseCorp类已经非常明确地只生产House产品,那为什么不定 义成House类型呢?ShanZhaiCorp就不同了,它确定不了生产什么类型。
好了,两大对应的阵营都已经产生了。我们再看Client程序,如代码清单29-13所示。
代码清单29-13 场景类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Client { public static void main(String[] args) { House house = new House(); System.out.println("-------房地产公司是这样运行的-------"); HouseCorp houseCorp =new HouseCorp(house); houseCorp.makeMoney(); System.out.println("\n"); System.out.println("-------山寨公司是这样运行的-------"); ShanZhaiCorp shanZhaiCorp = new ShanZhaiCorp(new IPod()); shanZhaiCorp.makeMoney(); } }
|
运行结果如下所示:
1 2 3 4 5 6 7 8
| -------房地产公司是这样运行的------- 生产出的房子是这样的... 生产出的房子卖出去了... 房地产公司赚大钱了... -------山寨公司是这样运行的------- 生产出的iPod是这个样子的... 生产出的iPod卖出去了... 我赚钱呀...
|
突然有一天,老板良心发现了,不准备生产这种“三无”产品了,那我们程序该怎么修改呢?如果仍重操旧业,生产衣服,那该如何处理呢?很容易处理,增加一个产品类,然后稍稍修改一下场景就可以了,我们来看衣服产品类,如代码清单29-14所示。
代码清单29-14 服装
1 2 3 4 5 6 7 8
| public class Clothes extends Product { public void beProducted() { System.out.println("生产出的衣服是这样的..."); } public void beSelled() { System.out.println("生产出的衣服卖出去了..."); } }
|
然后再稍稍修改一下场景类,如代码清单29-15所示。
代码清单29-15 场景类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Client { public static void main(String[] args) { House house = new House(); System.out.println("-------房地产公司是这样运行的-------"); HouseCorp houseCorp =new HouseCorp(house); houseCorp.makeMoney(); System.out.println("\n"); System.out.println("-------山寨公司是这样运行的-------"); ShanZhaiCorp shanZhaiCorp = new ShanZhaiCorp(new Clothes()); shanZhaiCorp.makeMoney(); } }
|
修改后的运行结果如下所示:
1 2 3 4 5 6 7 8
| -------房地产公司是这样运行的------- 生产出的房子是这样的... 生产出的房子卖出去了... 房地产公司赚大钱了... -------山寨公司是这样运行的------- 生产出的衣服是这样的... 生产出的衣服卖出去了... 我赚钱呀...
|
看代码中的黑体部分,就修改了这一条语句就完成了生产产品的转换。那我们深入思考一下,既然万物都是运动的,我现在只有房地产公司和山寨公司,那以后我会不会增加一些其他的公司呢?或者房地产公司会不会对业务进行细化,如分为公寓房公司、别墅公司,以及商业房公司等呢?那我告诉你,会的!绝对会的!但是你发觉没有,这种变化对我们上面的类图来说不会做大的修改,充其量只是扩展:
- 增加公司,要么继承Corp类,要么继承HouseCorp或ShanZhaiCorp,不用再修改原有的 类了。
- 增加产品,继承Product类,或者继承House类,你要把房子分为公寓房、别墅、商业 用房等。
你唯一要修改的就是Client类。类都增加了,高层模块也需要修改,也就是说Corp类和Product类都可以自由地扩展,而不会对整个应用产生太大的变更,这就是桥梁模式。