合成/聚合复用原则(CARP),尽量使用合成/聚合,尽量不要使用类继承。

对象的继承关系是在编译时就定好了,所以无法在运行时改变从父类继承的实现。子类的实现与它的父类有非常紧密的依赖关系,以至于父类实现中的任何变化必然导致子类发生变化。当你需要复用子类时,如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。

合成(Composition)和聚合(Aggregation)都是关联的特殊种类。
聚合表示一种弱的‘拥有’关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分;
合成表示一种强的‘拥有’关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。
比如大雁和雁群,就是聚合关系。大雁和翅膀,就是合成关系。

优先使用对象的合成/聚合关系将有助于保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小的规模,并且不太可能增长为不可控制的庞然大物。

桥接模式(Bridge),将抽象部分与它的实现部分分离,使它们都可以独立地变化。

Java复合类型是什么 java合成复用原则_java


举例:
实现发送多种消息,可以通过邮件、手机、系统内短信息发送,发送消息的种类有普通消息、加急消息和特急消息。

Java复合类型是什么 java合成复用原则_java_02


将各个消息类型进行抽象,实现部分就是具体的发送消息的方式。

  1. 发送方式接口
public interface MessageSend {
    public void send(String message, String user);
}
  1. 具体发送方法
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);
    }
}
  1. 抽象消息类型
public abstract class AbstractMessage {
    MessageSend messageSend;
    public AbstractMessage(MessageSend messageSend) {
        this.messageSend = messageSend;
    }
    public void sendMessage(String message, String user){
        messageSend.send(message, user);
    }
}
  1. 消息类型实现类
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);
    }
}
  1. 主程序
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("有事请假请速批", "王总");
    }
}
结果:
使用系统内短信息方法,发送消息:加班申请 给用户:李总
使用手机短信方法,发送消息:特急:有事请假请速批 给用户:王总

和工厂模式进行一个对比:

  1. 抽象工厂类
public interface IFactory {
    AbstractMessage createSmsMsg();
    AbstractMessage createMobileMsg();
}
  1. 抽象工厂实现类
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();
    }
}
  1. 抽象产品类
public abstract class AbstractMessage {
    public abstract void sendMessage(String message, String user);
}
  1. 抽象产品实现类
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);
    }
}
  1. 主程序
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的基础之上的。

Java复合类型是什么 java合成复用原则_java_03


应用系统作为一个等级结构,与JDBC驱动器这个等级结构是相对独立的,它们之间没有静态的强关联。应用系统通过委派与JDBC驱动器相互作用,这是一个桥梁模式的例子。

JDBC的这种架构,把抽象部分和具体部分分离开来,从而使得抽象部分和具体部分都可以独立地扩展。对于应用程序而言,只要选用不同的驱动,就可以让程序操作不同的数据库,而无需更改应用程序,从而实现在不同的数据库上移植;对于驱动程序而言,为数据库实现不同的驱动程序,并不会影响应用程序。

简单用代码实现一下:

  1. 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!");
    }

}
  1. 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();
    }
}
  1. 主程序
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!