[转]组合还是继承,这是一个问题
——由模式谈面向对象的原则之多用组合、少用继承
刚刚接触模式或者学习模式的人,经常会有这样的问题,为什么模式是成功的呢?很多人都会说模式是经验的积累,当然是正确的。可是经验为什么偏偏就证明了这种模式是正确的呢?这其中起用作的就是面向对象的基本原则。正是因为模式都或多或少的符合了面向对象的基本原则,所以模式才成为我们面向对象的设计和编码过程中不败的法则。那么什么是面向对象的基本原则呢?这就是我们将要一一讲到的问题。
单纯的讲到一个个的原则,就是那么的寥寥几句,非常的简单,但又是非常抽象的,难以理解。怎么办?
任何的理论,只要有生动的例子来讲解或证明,就能极大的帮助理解。所以我们准备从一个个的生动的例子来阐述我们的面向对象的基本原则。讲那些例子呢?上面我们说到,模式都是极大的遵从了这些原则的,那么我们把模式作为例子,来说明这些原则,不是我们信手拈来的吗?
现在我们说说其中的一个原则:对类的功能的扩展,要多用组合,少用继承。
对于类的扩展,在面向对象的编程过程中,我们首先想到的是类的继承,由子类继承父类,从而完成了对子类功能的扩展。但是,面向对象的原则告诉我们,对类的功能的扩展要多用组合,而少用继承。其中的原因有以下几点:
第一、 子类对父类的继承是全部的公有和受保护的继承,这使得子类可能继承了对子类无用甚至有害的父类的方法。换句话说,子类只希望继承父类的一部分方法,怎么办?
第二、 实际的对象千变万化,如果每一类的对象都有他们自己的类,尽管这些类都继承了他们的父类,但有些时候还是会造成类的无限膨胀。
第三、 继承的子类,实际上需要编译期确定下来,这满足不了需要在运行内才能确定对象的情况。而组合却可以比继承灵活得多,可以在运行期才决定某个对象。
嗨!光说这么多一二三有什么用,我们就是想看看实际情况是不是像上面说的那样呢?还是来看看实际的例子吧!
现在我们需要这样一个HashMap,它除了能按常规的Map那样取值,如get(Object obj)。还能按位取值,像ArrayList那样,按存入对象对的先后顺序取值。
对于这样一个问题,我们首先想到的是做一个类,它继承了HashMap类,然后用一个ArrayList属性来保存存入的key,我们按key的位来取值,代码如下:
public class ListMap extends HashMap {private List list;public ListMap() {super();this.list = new ArrayList();}public Object put(Object key, Object value) {if (list.contains(key)) {list.remove(key);}this.list.add(key);return super.put(key, value);}public Object getKey(int i) {return this.list.get(i);}public Object getValue(int i) {return this.get(getKey(i));}public int size() {return this.list.size();}}
public class MyListMap {private HashMap map;private List list;public MyListMap() {this.map = new HashMap();this.list = new ArrayList();}public Object put(Object key, Object value) {if (list.contains(key)) {list.remove(key);}this.list.add(key);return this.map.put(key, value);}public Object getKey(int i) {return this.list.get(i);}public Object getValue(int i) {return this.map.get(getKey(i));}public int size() {return this.list.size();}}
public interface WhatIHave { public void g();}public class Resource { public void f() { …… } public void h() { …… }}
Public class WhatIWant implements WhatIHave { private Resource res; public WhatIWant() { res = new Resource(); } public void g() { …… } public void f() { this.res.f(); } public void h() { this.res.h(); }}
public interface Product {public double money();}// 咖啡类:public class Coffee implements Product {public double money() {return 12;}}// 加糖:public class Sugar implements Product {private Product product;public Sugar(Product product) {this.product = product;}public double money() {return product.money + 2;}}// 加冰:public class Ice implements Product {private Product product;public Ice(Product product) {this.product = product;}public double money() {return product.money + 1.5;}}// 加奶:public class Milk implements Product {private Product product;public Milk(Product product) {this.product = product;}public double money() {return product.money + 4.0;}}// 加巧克力:public class Chocolate implements Product {private Product product;public Chocolate(Product product) {this.product = product;}public double money() {return product.money + 5.5;}}