Skip to content

Extend or Compostion

比方说,这里有一个动物类,作为当前宠物店的宠物信息的父类

javascript
class Animal {
  eat(Food)
}

现在有猫和狗,只需要让它们继承父类就可以共享eat

javascript
class Cat extends Animal {
  miao()
}
class Dot extends Animal {
  wof()
}

即使现在要添加run,也只需要修改父类即可

javascript
class Animal {
  eat(Food)
  run()
}

但是现在宠物店突然又想进货一批鱼儿,鱼儿没有跑动的行为,让它们继承Animal是不合适的,原来的抽象设计不适应现在新的设计了。

那么现在就需要大面积改动

javascript
class Animal {
  eat()
}
class GroundAnimal extends Animal {
  run()
}
class Cat extends GroundAnimal {
  miao()
}
class Dot extends GroundAnimal {
  wof()
}
class WaterAnimal {
  swim()
}
class Fish extends WaterAnimal {
  bubble()
}

,这时候再添加其他水生动物就没有问题了,继承WaterAnimal即可。

我们可以发现,只要刚开始的抽象设计足够合适,我们可以很方便的扩展。但是当发生了新的需求超过了原来的抽象需求的层次,就需要我们重新设计和变动,将是一件很麻烦的事情,如果这种变化影响面积大,发生频繁,将是一场灾难。

或许我们想要增强我们的设计系统以保证扩展性,但这样的话,即便不谈冗余设计带来的潜在浪费,谁能保证现在的需求能够非常周到地去考虑并满足未来的需求设计呢。

现在有另一种方式出现了,这是一种很松散的设计,我们知道这里有一些行为,一些可能被多个需要,或者只被单个引用。

typescript
function eat()
function miao()
function wof()
function run()

interface Dog {
  eat()
  wof()
  run()
}

interface Cat {
  eat()
  miao()
  run()
}

现在我们要添加鱼儿,只需要添加新的行为,并将原来的需要的行为组装即可。

typescript
function swim()
function bubble()
interface Fish {
  eat()
  bubble()
  swim()
}

这样不仅也能复用代码,也不会对原来的组织结构造成破坏,是一种很松散的组织,非常灵活,如果即便面临频繁地需求变更,也不会影响到原来的代码。

当然,我们也能看到缺点,我们对一个新的种类需要去重复的声明一些东西,当然这种影响不是最主要的,重要的还是我们必须对一种事物的内部十分地了解,才能较好的组成一个完整的事物。