产品经理吭哧吭哧跑过来:“xxx,我有个点子,balabalalbal,这么做很简单吧,就是一个小功能”。
程序员小强:“啊呀,这个功能要改不少代码啊。balabalabala 你看,这么多类都要改,要1周时间”。
产品经理:“不行啊,我们这个很关键,最好今天就上。或者要不明天早上?”
程序员小强:“滚😡”
本猿就遇到过这样子的情况,也许我们是维护的是一堆老代码,亦或者是自己给自己挖的坑。其实我们在拿到产品需求开始敲代码时,应该先仔细想想,到底应该怎么去做,然后又有哪些原则应该去遵循。
1. OO设计的基础
1.1 抽象与继承
抽象就是对我们代码中需要实体化的对象进行归纳抽象,找出共同点与不同点。比如书中说“游戏中会出现各种鸭子,有绿头鸭,有红头鸭,一边游泳戏水,一边呱呱叫”。在这,我们就可以抽象出一个“鸭子(Duck)”的基类出来。而子类,则为“绿头鸭(MallardDuck)”,“红头鸭(RedheadDuck)”。子类和基类之间则是继承关系。
1.2 多态 or 封装?
某天一早上产品经理来了:“我们的鸭子都要会飞(行为)”,这个时候一般来说很多人就想简单嘛,基类里加一个“飞(Fly)的方法”。
class Duck {void fly();}class MallardDuck extends Duck
过了一上午,产品经理又来了:“哦,我觉得我们还需要加上一些橡皮鸭,它不会飞(行为),也不会叫(行为),他是橡皮(类型)做的”。
很多时间我们大脑的第一反应就是多态嘛,**橡皮鸭覆盖飞(Fly方法。有一定经验的朋友们可能会想到,这是个无底洞,在给自己挖坑,如果又要加一个鸭子类型,然后忘记覆盖飞的方法了怎么办?
用接口(Interface)是否可以?
class MallardDuck implements Flyable
也许可以,但是还有一个问题,如果再加一个叫(Quack)的行为呢,而且每种鸭子叫的行为不一样? 那我们的代码就变成了:
class MallardDuck implements Flyable, Quackable
看起来,好像也很糟糕😰,接口里的每个子类方法都要实现一遍
设计原则:1. 把会变的地方“封装”起来,好让其他部分不受到影响。
行为:飞(Fly),叫(Quack)
设计原则:2. 针对接口编程,而不是针对实现(针对基类(超类型)编程)
行为放入专门的类中,此类只提供行为接口的实现。这样基类就不要知道行为的实现细节。**
interface FlyBehavior{}class FlyWithWings implements FlyBehavior {fly() {}}class FlyWithNoWings implements FlyBehavior {fly() {}}
这样一来,这些行为接口类也可以被其他类复用,比如鸟(Bird)类。
1.3 整合
委托(delegate):鸭子(Duck)将飞行(Fly)和叫(Quack)的行为委托给别的类(Behavior)处理。
class Duck {FlyBehavior flyBehavior;QuackBehavior quackBehavior;}class MallardDuck extendsw Duck {public MallardDuck() {flyBehavior = new FlyWithWings();}}
这个时候,我们发现,好像违反了设计原则2。那我们改怎么做么?
1.4 动态设定
class Duck {FlyBehavior flyBehavior;QuackBehavior quackBehavior;public void setFlyBehavior(FlyBehavior fb) {flyBehavior = fb;}}
2. OO设计的原则
总结一下:我们先抽象,再找关系(HAS-A,IS-A,IMPLEMENTS)
