在软件设计遵循的基本原则的有这么一条这样的原则:
多用组合少用继承!
在面向对象的软件设计中,对于类的扩展,首先想到的是使用类的继承来实现,由子类继承父类,从而完成对子类的功能扩展。
继承的好处是可以尽量让相同的属性或功能复用。但是随着项目越来越大,需求不断编号,继承就会变得越来越臃肿,后期难以控制和维护。
最重要的是,继承会不分青红皂白地把父类的公有和受保护的方法统统继承下来,而这些方法可能是子类不需要的功能,会对子类产生一些危害!
如果使用组合方式,就不会出现上述继承问题。所谓对象组合,是指在一个对象中含有另外一个对象的引用,从而可以使用该内部对象的引用
作出一些处理行为。
使用组合方式的好处有以下几点:
首先,不会对类产生有害的影响;
其次,组合方式要比继承方式灵活,因为是有系统运行动态地决定使用对象与否。
最后,不会造成因继承而引起的类的膨胀,减少了对父类的依赖性!
以上部分摘抄于《软件秘笈-设计模式那点事》第一章第四页!
以下着重介绍一下组合,继承因为大家都很熟悉所以在此不作为重点介绍!
其实组合大家也都在用,只是不知道其理论而已!
package test;
public class Person {
private Head h ;
private Body b ;
public Person(){
h = new Head();
b = new Body();
}
public Head getH() {
return h;
}
public Body getB() {
return b;
}
}
class Head {
public Head() {
System.out.println(" head ");
}
}
class Body {
public Body() {
System.out.println(" body ");
}
}
小刚本人是从事网络游戏行业的,故在此用游戏相关的业务对象来进行说明!
比如说开发一款角色扮演类的网络游戏
游戏中常常会设计到游戏用户,游戏主角色,主角色中的伙伴(宠物,侠客,家将);游戏NPC,游戏怪物等对象!
1、所有游戏角色 抽象 Ispiriter
/**所有游戏角色的总抽象*/
public interface ISpiriter {
}
/**游戏账户 与 游戏角色 1-1 的设计*/
public class Hero implements ISpiriter{
/**玩家伙伴(侠客、美人、宠物)*/
private List<IHuoban> huobans;
/**技能信息*/
private Skill skillInfo;
/**玩家扩展信息*/
private HeroExtra heroExtra;
/**玩家基本属性 这个是从配置表 或玩家当前装备等属性加成系统中得来*/
private HeroAttr heroAttr;
/**玩家基本行会信息*/
private Guild guildinfo;
/**通过数据库自动生成*/
private long heroId;
/**在创建游戏角色的时候由前端传入*/
private String openid;
/**在创建游戏角色的时候由前端传入*/
private String pf;
/**角色等级*/
private int level;
}
3、游戏中的怪物
/** 怪物 */
public class Monster implements ISpiriter {
/** 怪物id */
private long monsterId;
/** 怪物的属性 及其其他信息 */
private <span style="font-family: Arial, Helvetica, sans-serif;">MonsterAttr </span><span style="font-family: Arial, Helvetica, sans-serif;">monsterAttr;</span>
}
4、游戏中的伙伴抽象
/**主角色的伙伴抽象 (包括 美人 侠客 灵宠)*/
public interface IHuoban extends ISpiriter {
}
5、
/**美人*/
public class Beauty implements IHuoban {
}
/**灵宠*/
public class Pet implements IHuoban {
}
/**侠客*/
public class Xiake implements IHuoban {
}
以上设计还望大家可以指点一下!
再论组合与继承
在面向对象的程序设计中,创建和使用代码最可能采取的一种做法是:将数据和方法统一封装到一个类里,并且使用那个类的对象。有些时候,需通过“组合”技术用现成的类来构造新类。而继承是最少见的一种做法。
因此,尽管继承在学习OOP的过程中得到了大量的强调,但并不意味着应该尽可能地到处使用它。相反,使用它时要特别慎重。只有在清楚知道继承在所有方法中最有效的前提下,才可考虑它。为判断自己到底应该选用组合还是继承,一个最简单的办法就是考虑是否需要从新类上溯造型回基础类。若必须上溯,就需要继承。但如果不需要上溯造型,就应提醒自己防止继承的滥用。但只要记住经常问自己!
首先解释一下什么叫上溯造型!(即子类可以强制转换成父类)
例如:
fight(ISpiriter f1,Ispiriter f2){}我们可以传入hero对象和Monster对象!这里传入的hero与monster对象被转成了父类,
其方法调用的接口是不是就变窄了很多!上溯造型是安全的类型转换!
下面我们可以用几个简单的例子决定在什么场景中去使用继承和组合!
/**昆虫*/
class Insect {
/**大小*/
private int size;
/**颜色*/
private String color;
public Insect(int size, String color) {
this.size = size;
this.color = color;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public void move() {
System.out.println("移动到合适位置");
}
//假设昆虫必须要先移动一次 到合适为止 才能进行攻击
public void attack() {
move();
System.out.println("--发起攻击");
}
}
class Bee extends Insect {
public Bee(int size, String color) {
super(size, color);
}
/**蜜蜂的移动方式:飞*/
public void move() {
System.out.println("飞到合适位置");
}
public void attack() {
move();
super.attack();
}
}
public class MainTest1 {
public static void main(String[] args) {
Insect i = new Bee(1, "red");
i.attack();
}
}
输出的结果竟然是:
飞到合适位置
飞到合适位置
--发起攻击
使用组合方式的实现:
class Attack implements IAttack {
private String move;
private String attack;
public Attack(String move, String attack) {
this.move = move;
this.attack = attack;
}
@Override
public void move() {
System.out.println(move);
}
@Override
public void attack() {
move();
System.out.println(attack);
}
}
class Insect {
private int size;
private String color;
public Insect(int size, String color) {
this.size = size;
this.color = color;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
class Bee extends Insect implements IAttack {
private IAttack attack;
public Bee(int size, String color, IAttack attack) {
super(size, color);
this.attack = attack;
}
public void move() {
attack.move();
}
public void attack() {
attack.attack();
}
}
public class MainTest2 {
public static void main(String[] args) {
Bee a = new Bee(1, "red", new Attack("飞到合适位置", "--发起攻击"));
a.attack();
}
}
总结:
如果一个类需要向另一类暴露所有方法 或者属性的 就需要用继承,而前者为父类,后者为子类 比如上面例子中的
Insect(昆虫)与 Bee(蜜蜂)
如果一个类只需要另一个类的部分功能或者属性 则用组合。
本人设计的游戏业务案例中Hero类便是如此!
还有在j2ee 中常用的hibernate 框架的实体类 采用的均是典型的组合方式编程!