合成/聚合复用原则(CARP),尽量使用合成/聚合,尽量不要使用类继承。
对象的继承关系是在编译时就定好了,所以无法在运行时改变从父类继承的实现。子类的实现与它的父类有非常紧密的依赖关系,以至于父类实现中的任何变化必然导致子类发生变化。当你需要复用子类时,如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。
合成(Composition)和聚合(Aggregation)都是关联的特殊种类。
聚合表示一种弱的‘拥有’关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分;
合成表示一种强的‘拥有’关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。
比如大雁和雁群,就是聚合关系。大雁和翅膀,就是合成关系。
优先使用对象的合成/聚合关系将有助于保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小的规模,并且不太可能增长为不可控制的庞然大物。
桥接模式(Bridge),将抽象部分与它的实现部分分离,使它们都可以独立地变化。
举例:
实现发送多种消息,可以通过邮件、手机、系统内短信息发送,发送消息的种类有普通消息、加急消息和特急消息。
将各个消息类型进行抽象,实现部分就是具体的发送消息的方式。
- 发送方式接口
public interface MessageSend {
public void send(String message, String user);
}
- 具体发送方法
public class MessageEmail implements MessageSend {
@Override
public void send(String message, String user) {
System.out.println("使用邮箱发送方法,发送消息:" + message + " 给用户:" +user);
}
}
public class MessageMobile implements MessageSend {
@Override
public void send(String message, String user) {
System.out.println("使用手机短信方法,发送消息:" + message + " 给用户:" +user);
}
}
public class MessageSMS implements MessageSend {
@Override
public void send(String message, String user) {
System.out.println("使用系统内短信息方法,发送消息:" + message + " 给用户:" +user);
}
}
- 抽象消息类型
public abstract class AbstractMessage {
MessageSend messageSend;
public AbstractMessage(MessageSend messageSend) {
this.messageSend = messageSend;
}
public void sendMessage(String message, String user){
messageSend.send(message, user);
}
}
- 消息类型实现类
public class CommonMessage extends AbstractMessage {
public CommonMessage(MessageSend messageSend) {
super(messageSend);
}
}
public class UrgencyMessage extends AbstractMessage {
public UrgencyMessage(MessageSend messageSend) {
super(messageSend);
}
@Override
public void sendMessage(String message, String user) {
message = "加急:" + message;
super.sendMessage(message, user);
}
}
public class SpecialUrgencyMessage extends AbstractMessage {
public SpecialUrgencyMessage(MessageSend messageSend) {
super(messageSend);
}
@Override
public void sendMessage(String message, String user) {
message = "特急:" + message;
super.sendMessage(message, user);
}
}
- 主程序
public class Test {
public static void main(String[] args) {
// 发送系统内短消息
MessageSend messageSend = new MessageSMS();
// 创建普通发送类型
AbstractMessage message = new CommonMessage(messageSend);
message.sendMessage("加班申请", "李总");
// 发送短信
messageSend = new MessageMobile();
// 特急
message = new SpecialUrgencyMessage(messageSend);
message.sendMessage("有事请假请速批", "王总");
}
}
结果:
使用系统内短信息方法,发送消息:加班申请 给用户:李总
使用手机短信方法,发送消息:特急:有事请假请速批 给用户:王总
和工厂模式进行一个对比:
- 抽象工厂类
public interface IFactory {
AbstractMessage createSmsMsg();
AbstractMessage createMobileMsg();
}
- 抽象工厂实现类
public class CommonMessageFactory implements IFactory {
@Override
public AbstractMessage createSmsMsg() {
return new SmsCommonMsg();
}
@Override
public AbstractMessage createMobileMsg() {
return new MobileCommonMsg();
}
}
public class UrgMessageFactory implements IFactory {
@Override
public AbstractMessage createSmsMsg() {
return new SmsUrgMsg();
}
@Override
public AbstractMessage createMobileMsg() {
return new MobileUrgMsg();
}
}
- 抽象产品类
public abstract class AbstractMessage {
public abstract void sendMessage(String message, String user);
}
- 抽象产品实现类
public class MobileCommonMsg extends AbstractMessage {
@Override
public void sendMessage(String message, String user) {
System.out.println("用手机发送普通消息:" + message + " 给" + user);
}
}
public class MobileUrgMsg extends AbstractMessage {
@Override
public void sendMessage(String message, String user) {
System.out.println("用手机发送特急消息:" + message + " 给" + user);
}
}
public class SmsCommonMsg extends AbstractMessage {
@Override
public void sendMessage(String message, String user) {
System.out.println("用站内短信息发送普通消息:" + message + " 给" + user);
}
}
public class SmsUrgMsg extends AbstractMessage {
@Override
public void sendMessage(String message, String user) {
System.out.println("用站内短信息发送特急消息:" + message + " 给" + user);
}
}
- 主程序
public class Test {
public static void main(String[] args) {
IFactory factory = new CommonMessageFactory();
AbstractMessage msg = factory.createSmsMsg();
msg.sendMessage("加班申请", "李总");
factory = new UrgMessageFactory();
msg = factory.createMobileMsg();
msg.sendMessage("有事请假请速批", "王总");
}
}
运行结果:
用站内短信息发送普通消息:加班申请 给李总
用手机发送特急消息:有事请假请速批 给王总
能看出来,在这个例子当中,采用抽象工厂模式,代码量要比桥接模式大了许多,而且没有桥接模式灵活。
桥接模式在Java中的应用
桥梁模式在Java应用中的一个非常典型的例子就是JDBC驱动器。JDBC为所有的关系型数据库提供一个通用的界面。一个应用系统动态地选择一个合适的驱动器,然后通过驱动器向数据库引擎发出指令。这个过程就是将抽象角色的行为委派给实现角色的过程。
抽象角色可以针对任何数据库引擎发出查询指令,因为抽象角色并不直接与数据库引擎打交道,JDBC驱动器负责这个底层的工作。由于JDBC驱动器的存在,应用系统可以不依赖于数据库引擎的细节而独立地演化;同时数据库引擎也可以独立于应用系统的细节而独立的演化。两个独立的等级结构如下图所示,左边是JDBC API的等级结构,右边是JDBC驱动器的等级结构。应用程序是建立在JDBC API的基础之上的。
应用系统作为一个等级结构,与JDBC驱动器这个等级结构是相对独立的,它们之间没有静态的强关联。应用系统通过委派与JDBC驱动器相互作用,这是一个桥梁模式的例子。
JDBC的这种架构,把抽象部分和具体部分分离开来,从而使得抽象部分和具体部分都可以独立地扩展。对于应用程序而言,只要选用不同的驱动,就可以让程序操作不同的数据库,而无需更改应用程序,从而实现在不同的数据库上移植;对于驱动程序而言,为数据库实现不同的驱动程序,并不会影响应用程序。
简单用代码实现一下:
- Driver接口及其实现类
interface Driver {
void add();
void update();
}
class MysqlDriver implements Driver {
@Override
public void add() {
System.out.println("Mysql add!");
}
@Override
public void update() {
System.out.println("Mysql update!");
}
}
class OracleDriver implements Driver {
@Override
public void add() {
System.out.println("Oracle add!");
}
@Override
public void update() {
System.out.println("Oracle update!");
}
}
- JDBC API 抽象类及其实现类
abstract class JdbcApi {
Driver driver;
public JdbcApi(Driver driver) {
this.driver = driver;
}
abstract void add();
abstract void update();
}
class MysqlApp extends JdbcApi {
public MysqlApp(Driver driver) {
super(driver);
}
@Override
public void add() {
System.out.println("我需要操作Mysql数据库添加数据");
driver.add();
}
@Override
public void update() {
System.out.println("我需要操作Mysql数据库更新数据");
driver.update();
}
}
class MysqlApp extends JdbcApi {
public MysqlApp(Driver driver) {
super(driver);
}
@Override
public void add() {
System.out.println("我需要操作Mysql数据库添加数据");
driver.add();
}
@Override
public void update() {
System.out.println("我需要操作Mysql数据库更新数据");
driver.update();
}
}
- 主程序
public class Test {
public static void main(String[] args) {
Driver driver = new MysqlDriver();
JdbcApi app = new MysqlApp(driver);
app.add();
app.update();
driver = new OracleDriver();
app = new OracleApp(driver);
app.add();
app.update();
}
}
运行结果:
我需要操作Mysql数据库添加数据
Mysql add!
我需要操作Mysql数据库更新数据
Mysql update!
我需要操作Oracle数据库添加数据
Oracle add!
我需要操作Oracle数据库更新数据
Oracle update!