单例设计模式(singleton)最常用、最简单的设计模式。单例模式的目的是保证在整个应用中某一个类有且只有一个实例(一个类在堆内存只存在一个对象)。怎么样让一个类一个类有且只有一个实例呢?最核心的就是一句话就是构造方法私有化。单例模式的编写有很多种写法。比如饿汉式、懒汉式、双重加锁机制静态内部类、枚举。
饿汉式,从名字上理解像是有个人很容易饿,所以他每次不管自己饿不饿,没事就提前把要吃的东西先准备出来,也就是 “比较勤”,所以实例在初始化的时候就已经建好了,不管你有没有用到,都先建好了再说。
[1] 构造方法私有化,防止外界通过构造器创建新的工具类对象;
[2] 必须在该类中,自己先创建出一个对象;
[3] 向外暴露一个公共的静态方法用于返回自身的对象;
// 单例模式(饿汉式)
public class Singleton1 {
// [1]构造方法私有化,防止外界通过构造器创建新的工具类对象
private Singleton1() {
}
// [2] 必须在该类中,自己先创建出一个对象(这行代码在类加载的时候就执行了)
private static Singleton1 instance = new Singleton1();
// [3] 向外暴露一个公共的静态方法用于返回自身的对象
public static Singleton1 getSingleton() {
return instance;
}
}
注意:我们知道,类加载的方式是按需加载,且加载一次。因此,在上述单例类被加载时,就会实例化一个对象并交给自己的引用,供系统使用;而且,由于这个类在整个生命周期中只会被加载一次,因此只会创建一个实例,即能够充分保证单例。
好处:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
坏处:在类装载的时候就完成实例化,没有达到Lazy Loading(懒加载)的效果。 如果从始至终从未使用过这个实例,则会造成内存的浪费。
懒汉式,顾名思义就像一个人比较懒,平时不爱动,事情火烧眉毛了才迫不得已去做,“比较懒”,用的时候才去检查有没有实例,如果有则返回,没有则新建。有线程安全和线程不安全两种写法,区别就是synchronized关键字。
[1] 构造方法私有化,防止外界通过构造器创建新的工具类对象
[2] 事先创建好当前类的一个私有静态对象
[3] 向外暴露一个公共的静态方法用于返回自身的对象
// 单例模式(懒汉式)
public class Singleton2 {
// [1]私有化构造方法。
private Singleton2() {
}
// [2] 事先创建好当前类的一个私有静态对象
private static Singleton2 instance = null;
// [3] 向外暴露一个公共的静态方法用于返回自身的对象
public static Singleton2 getInstance() {
// 被动创建,在真正需要使用时才去创建
if (null == instance) {
instance= new Singleton2();
}
return instance;
}
}
注意:我们从懒汉式单例可以看到,单例实例被延迟加载,即只有在真正使用的时候才会实例化一个对象并交给自己的引用。
优缺点:这种写法起到了Lazy Loading(懒加载)的效果,但是只能在单线程下使用。如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。
为了解决上面的问题,最简单的方法是将整个 getInstance() 方法设为同步(synchronized)。
// 单例模式(懒汉式)
public class Singleton2 {
// [1]私有化构造方法。
private Singleton2() {
}
// [2] 事先创建好当前类的一个私有静态对象
private static Singleton2 instance = null;
// [3] 向外暴露一个公共的静态方法用于返回自身的对象
public static synchronized Singleton2 getInstance() {
// 被动创建,在真正需要使用时才去创建
if (null == instance) {
instance= new Singleton2();
}
return instance;
}
}
注意:虽然做到了线程安全,并且解决了多实例的问题,但是它并不高效。因为同步操作只需要在第一次调用时才被需要,即第一次创建单例实例对象时,也就是if条件判断里面的内容。这就引出了双重检验锁。
双重检验锁,又叫双重校验锁,综合了懒汉式和饿汉式两者的优缺点整合而成。看上面代码实现中,特点是在synchronized关键字内外都加了一层 if 条件判断,这样既保证了线程安全,又比直接上锁提高了执行效率,还节省了内存空间。
// 单例模式(懒汉式)
public class Singleton3 {
private static Singleton3 instance;
private Singleton3 (){}
public static Singleton3 getSingleton() {
if (instance == null) {
synchronized (Singleton3.class) {
if (instance == null) {
instance = new Singleton3();
}
}
}
return instance;
}
}
public class Singleton4 {
private static class SingletonHolder {
private static final Singleton4 INSTANCE = new Singleton4();
}
private Singleton4 (){}
public static final Singleton4 getInstance() {
return SingletonHolder.INSTANCE;
}
}
注意:这种写法仍然使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。
public enum EasySingleton{
// 定义枚举常量
INSTANCE;
}
使用:我们可以通过EasySingleton.INSTANCE.工具方法() 的方式来访问实例,这比调用getInstance()方法简单多了。创建枚举默认就是线程安全的,所以不需要担心double checked locking,而且还能防止反序列化导致重新创建新的对象。
1)网站的计数器,一般也是采用单例模式实现,否则难以同步。
2)应用程序的日志应用,一般都是单例模式实现,只有一个实例去操作才好,否则内容不好追加显示。
3)多线程的线程池的设计一般也是采用单例模式,因为线程池要方便对池中的线程进行控制
4)Windows的(任务管理器)就是很典型的单例模式,他不能打开俩个
5)windows的(回收站)也是典型的单例应用。在整个系统运行过程中,回收站只维护一个实例。
工厂模式提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。实现了创建者和调用者分离,工厂模式分为简单工厂、工厂方法、抽象工厂模式。
简单工厂模式又叫静态方法模式(因为工厂类定义了一个静态方法)现实生活中,工厂是负责生产产品的;同样在设计模式中,简单工厂模式我们可以理解为负责生产对象的一个类,称为“工厂类”。
将“类实例化的操作”与“使用对象的操作”分开,让使用者不用知道具体参数就可以实例化出所需要的“产品”类,从而避免了在客户端代码中显式指定,实现了解耦。即使用者可直接消费产品而不需要知道其生产的细节
简单工厂模式:把对象的创建放到一个工厂类中,通过参数来创建不同的对象。
/**
* 简单工厂类
*/
public class SimpleFactory {
/**
* 生产工厂
*/
public static OSSConfig createOSSConfig(String name) {
OSSConfig config = null;
switch (name) {
case "alibaba":
config = new AlibabaOSSConfig();
break;
case "tencent":
config = new TencentOSSConfig();
break;
default:
break;
}
return config;
}
}
将创建实例的工作与使用实例的工作分开,使用者不必关心类对象如何创建,实现了解耦;
把初始化实例时的工作放到工厂里进行,使代码更容易维护。 更符合面向对象的原则 & 面向接口编程,而不是面向实现编程。
1)工厂类集中了所有实例(产品)的创建逻辑,一旦这个工厂不能正常工作,整个系统都会受到影响;
2)违背“开放 - 关闭原则”,一旦添加新产品就不得不修改工厂类的逻辑,这样就会造成工厂逻辑过于复杂。
3)简单工厂模式由于使用了静态工厂方法,静态方法不能被继承和重写,会造成工厂角色无法形成基于继承的等级结构。
客户如果只知道传入工厂类的参数,对于如何创建对象的逻辑不关心时;
当工厂类负责创建的对象(具体产品)比较少时。
工厂方法模式,又称工厂模式、多态工厂模式和虚拟构造器模式,通过定义工厂父类负责定义创建对象的公共接口,而子类则负责生成具体的对象。
工厂方法模式: 每种产品由一种工厂来创建,一个工厂处理一个对象。
角色组成
1. 抽象工厂: 定义工厂类所具有的基本的操作
2. 具体工厂:该类必须继承抽象工厂,实现抽象工厂定义的方法,返回一个对象
3. 抽象产品:定义了抽象产品具有的基本操作
4. 产品实现类:实现抽象产品类对定义的抽象方法,和具体工厂一一对应;
/**
* 抽象产品: 配置接口
*/
interface OSSConfig {
/**
* 初始化OSS配置
*/
void init();
}
/**
* 具体产品: 阿里云OSS配置
*/
class AlibabaOSSConfig implements OSSConfig {
@Override
public void init() {
System.out.println("初始化--阿里云--OSS配置");
}
}
/**
* 具体产品: 腾讯云OSS配置
*/
class TencentOSSConfig implements OSSConfig {
@Override
public void init() {
System.out.println("初始化--腾讯云--OSS配置");
}
}
/**
* 抽象工厂
*/
interface Factory {
OSSConfig createOSSConfig();
}
/**
* 具体工厂: 腾讯云工厂
*/
class TencentOSSFactory implements Factory {
@Override
public OSSConfig createOSSConfig() {
return new TencentOSSConfig();
}
}
/**
* 具体工厂: 阿里云工厂
*/
class AlibabaOSSFactory implements Factory {
@Override
public OSSConfig createOSSConfig() {
return new AlibabaOSSConfig();
}
}
/**
* 工厂设计模拟类
*/
public class FactoryMethod {
public static void main(String[] args) {
AlibabaOSSFactory alibabaOSSFactory = new AlibabaOSSFactory();
OSSConfig alibabaConifg = alibabaOSSFactory.createOSSConfig();
alibabaConifg.init();
TencentOSSFactory tencentOSSFactory = new TencentOSSFactory();
OSSConfig tencentConfig = tencentOSSFactory.createOSSConfig();
tencentConfig.init();
}
}
工厂方法模式把具体产品的创建推迟到工厂类的子类(具体工厂)中,此时工厂类不再负责所有产品的创建,而只是给出具体工厂必须实现的接口,这样工厂方法模式在添加新产品的时候就不修改工厂类逻辑而是添加新的工厂子类,符合开放封闭原则,克服了简单工厂模式中缺点。
1)更符合开-闭原则
新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可
简单工厂模式需要修改工厂类的判断逻辑
2)符合单一职责原则
每个具体工厂类只负责创建对应的产品
简单工厂中的工厂类存在复杂的switch逻辑判断
3)不使用静态工厂方法,可以形成基于继承的等级结构。
简单工厂模式的工厂类使用静态工厂方法
总结:工厂模式可以说是简单工厂模式的进一步抽象和拓展,在保留了简单工厂的封装优点的同时,让扩展变得简单,让继承变得可行,增加了多态性的体现。
1)添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销;
2)由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。
3)虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类;一个具体工厂只能创建一种具体产品。
1)当一个类不知道它所需要的对象的类时,在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可;
2)当一个类希望通过其子类来指定创建对象时,在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
3)将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。
抽象工厂模式,即Abstract Factory Pattern,提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类;具体的工厂负责实现具体的产品实例。 允许使用抽象的接口来创建一组相关产品,而不需要知道或关心实际生产出的具体产品是什么,这样就可以从具体产品中被解耦。
抽象工厂模式:工厂方法模式的进一步延伸。
角色组成
1. 抽象工厂: 定义工厂类所具有的基本的操作
2. 具体工厂:具体工厂实现了抽象工厂,每个工厂方法返回多个具体对象
3. 抽象类接口:定义了产品具有的基本操作,提供一组所有类都具有的业务方法
4. 抽象类:用于实现抽象接口所定义的业务方法,只做抽象接口,具体由产品实现类处理
5. 产品实现类:实现抽象产品类对定义的抽象方法,和具体工厂一对多;
/**
* 抽象产品: 配置接口
*/
interface OSSConfig {
/**
* 初始化OSS配置
*/
void init();
}
/**
* 具体产品: 阿里云OSS配置
*/
abstract class AlibabaOSSConfig implements OSSConfig {
public abstract void init();
}
/**
* 具体产品: 阿里云OSS配置Test环境
*/
class AlibabaTstOSSConfig extends AlibabaOSSConfig {
@Override
public void init() {
System.out.println("初始化--阿里云--test环境--OSS配置");
}
}
/**
* 具体产品: 阿里云OSS配置生产环境
*/
class AlibabaPreOSSConfig extends AlibabaOSSConfig {
@Override
public void init() {
System.out.println("初始化--阿里云--Pre环境--OSS配置");
}
}
/**
* 抽象产品: 腾讯云OSS配置
*/
abstract class TencentOSSConfig implements OSSConfig {
public abstract void init();
}
/**
* 抽象产品: 腾讯云OSS配置
*/
class TencentProOSSConfig extends TencentOSSConfig {
@Override
public void init() {
System.out.println("初始化--腾讯云--Pre环境--OSS配置");
}
}
/**
* 抽象工厂
*/
interface Factory {
/**
* 获取测试环境配置
*/
OSSConfig createOSSTestConfig();
/**
* 获取生成环境配置
*/
OSSConfig createOSSPreConfig();
}
/**
* 具体工厂: 腾讯云工厂
*/
class TencentOSSFactory implements Factory {
@Override
public OSSConfig createOSSTestConfig() {
return null;
}
@Override
public OSSConfig createOSSPreConfig() {
return new TencentProOSSConfig();
}
}
/**
* 具体工厂: 腾讯云工厂
*/
class AlibabaOSSFactory implements Factory {
@Override
public OSSConfig createOSSTestConfig() {
return new AlibabaTstOSSConfig();
}
@Override
public OSSConfig createOSSPreConfig() {
return new AlibabaPreOSSConfig();
}
}
/**
* 工厂设计模拟类
* 产品接口可以定义OSS配置的操作接口,不同的厂商实现都去实现接口,通过工厂动态生成配置
*/
public class AbstractFactory {
public static void main(String[] args) {
// 阿里云oss配置
AlibabaOSSFactory alibabaOSSFactory = new AlibabaOSSFactory();
OSSConfig alibabaConifg = alibabaOSSFactory.createOSSPreConfig();
alibabaConifg.init();
OSSConfig alibabaTestConifg = alibabaOSSFactory.createOSSTestConfig();
alibabaTestConifg.init();
// 腾云云oss配置
TencentOSSFactory tencentOSSFactory = new TencentOSSFactory();
OSSConfig tencentConifg = tencentOSSFactory.createOSSPreConfig();
tencentConifg.init();
}
}
1)降低耦合
抽象工厂模式将具体产品的创建延迟到具体工厂的子类中,这样将对象的创建封装起来,可以减少客户端与具体产品类之间的依赖,从而使系统耦合度低,这样更有利于后期的维护和扩展;
2)更符合开-闭原则
新增一种产品类时,只需要增加相应的具体产品类和相应的工厂子类即可
3)符合单一职责原则
每个具体工厂类只负责创建对应的产品
4)不使用静态工厂方法,可以形成基于继承的等级结构。
抽象工厂模式很难支持新种类产品的变化。这是因为抽象工厂接口中已经确定了可以被创建的产品集合,如果需要添加新产品,此时就必须去修改抽象工厂的接口,这样就涉及到抽象工厂类的以及所有子类的改变,这样也就违背了“开发——封闭”原则。
1)一个系统不要求依赖产品类实例如何被创建、组合和表达的表达,这点也是所有工厂模式应用的前提。
2)这个系统有多个系列产品,而系统中只消费其中某一系列产品
3)系统要求提供一个产品类的库,所有产品以同样的接口出现,客户端不需要依赖具体实现。
工厂方法模式:具体工厂类只能创建一个具体产品类的实例。
抽象工厂模式:具体工厂类可以创建多个具体产品类的实例。
给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用。其中代理对象起到中介作用,用于连接客户端和目标对象。例如:电脑桌面的快捷方式。电脑对某个程序提供一个快捷方式(代理对象),快捷方式连接客户端和程序,客户端通过操作快捷方式就可以操作那个程序。
通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性。
步骤1: 创建抽象对象接口(Subject):声明你(真实对象)需要让代购(代理对象)帮忙做的事(买Mac)
public interface Subject {
public void buyMac();
}
步骤2: 创建真实对象类(RealSubject),即”我“
public class RealSubject implement Subject{
@Override
public void buyMac() {
System.out.println(”买一台Mac“);
}
}
步骤3:创建代理对象类(Proxy),即”代购“,并通过代理类创建真实对象实例并访问其方法
public class Proxy implements Subject{
@Override
public void buyMac{
//引用并创建真实对象实例,即”我“
RealSubject realSubject = new RealSubject();
//调用真实对象的方法,进行代理购买Mac
realSubject.buyMac();
//代理对象额外做的操作
this.WrapMac();
}
public void WrapMac(){
System.out.println(”用盒子包装好Mac“);
}
}
步骤4:客户端调用
public class ProxyPattern {
public static void main(String[] args){
Subject proxy = new Proxy();
proxy.buyMac();
}
}
结果输出
买一台Mac
用盒子包装好Mac
优点
协调调用者和被调用者,降低了系统的耦合度
代理对象作为客户端和目标对象之间的中介,起到了保护目标对象的作用
缺点
由于在客户端和真实主题之间增加了代理对象,因此会造成请求的处理速度变慢;
实现代理模式需要额外的工作(有些代理模式的实现非常复杂),从而增加了系统实现的复杂度。
在静态代理模式中一个静态代理只服务一种类型的目标对象,若要服务多类型的目标对象,则需要为每种目标对象都实现一个静态代理对象。在目标对象较多的情况下,若采用静态代理,则会出现 静态代理对象量多、代码量大,从而导致代码复杂的问题。
动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作。
代理类在程序运行期间,创建的代理对象称之为动态代理对象。这种情况下,创建的代理对象,并不是事先在Java代码中定义好的。而是在运行期间,根据我们在动态代理对象中的“指示”,动态生成的。也就是说,你想获取哪个对象的代理,动态代理就会为你动态的生成这个对象的代理对象。动态代理可以对被代理对象的方法进行功能增强。有了动态代理的技术,那么就可以在不修改方法源码的情况下,增强被代理对象的方法的功能,在方法执行前后做任何你想做的事情。
1)动态代理不需要显式实现与目标对象类(RealSubject)相同的接口,而是将这种实现推迟到程序运行时由 JVM来实现。即:在使用时再创建动态代理类 & 实例;
2)通过Java 反射机制的method.invoke(),通过调用动态代理类对象方法,从而自动调用目标对象的方法。
优点
1)只需要1个动态代理类就可以解决创建多个静态代理的问题,避免重复、多余代码;
2)更强的灵活性;
缺点
1)效率低:相比静态代理中直接调用目标对象方法,动态代理则需要先通过Java反射机制 从而间接调用目标对象方法。
2)应用场景局限:Java 的单继承特性(每个代理类都继承了 Proxy 类),即只能针对接口创建代理类,不能针对类创建代理类。
日志记录、性能统计、安全控制、异常处理等。
1.创建接口,定义目标类需要完成的功能
2.创建目标类,实现接口。
3.创建InvocationHandler接口的实现类。在invoke方法中完成代理类的功能。
目标方法的调用
功能增强
4.使用Proxy类中静态方法Proxy.newProxyInstance完成代理类对象的创建,返回代理对象,并把返回值转为接口类型。
public class JDKDynamicProxy {
public static void main(String[] args) {
CAProxy caProxy = new CAProxy();
IA instance = (IA) caProxy.getInstance(new CA());
instance.say();
instance.fly();
}
}
interface IA{
void say();
void fly();
}
class CA implements IA{
@Override
public void say() {
System.out.println("I am class CA");
}
@Override
public void fly() {
System.out.println("I can fly");
}
}
class CAProxy implements InvocationHandler{
private Object target;
public Object getInstance(Object object){
this.target = object;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("I am proxy!");
Object result = method.invoke(target, args);
return result;
}
}
public class CglibDynamicProxy {
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
A a = (A) cglibProxy.getInstance(new A());
a.say();
}
}
class A {
public void say(){
System.out.println("I am A");
}
}
class CglibProxy implements MethodInterceptor{
private Object target;
public Object getInstance(Object object){
this.target = object;
Enhancer enhancer = new Enhancer();
// 设置父类为实例类
enhancer.setSuperclass(this.target.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("I am cglib proxy!");
Object result = method.invoke(target, objects);
return result;
}
}
设计模式(Design pattern)是一种思想,是一套被反复使用的代码设计经验总结,专门用于解决特定场景的需求。它提供了在软件开发过程中面临的一些问题的最佳解决方案,使用设计模式是为了提高代码的可重用性、让代码通俗易懂,增加代码可靠性。
开闭原则:对扩展开放,对修改关闭。就是如果要修改原有的功能或者是扩展功能,尽量去扩展原有的代码,而不是修改原来已有的代码。
里氏替换原则:任何子类对象都应该可以替换其派生的超类对象 。即,子类可以扩展父类的功能,但不要修改父类原有的功能。 也就是说,当一个子类继承父类后,尽量不要去重写它原有的方法。
依赖转置(依赖倒置)原则:要面向接口编程,不要面向实现编程。两个模块交互时,都访问各自接口,而不是具体的实现类。
单一职责原则:一个对象要专注于一种事情,不要让它担任太多责任。
接口隔离原则:一个接口尽量只包含用户关心的内容。就是一个接口不要太庞大。
迪米特法则:如果两个软件实体之间不是特别必要,尽量不要让他们直接通信。而是找个第三方进行转发,比如使用MQ(消息队列)。
合成复用原则:如果在“组合/聚合”和“继承”之间做抉择时,优先选择“组合/聚合”。
创建型模式:用于创建对象的设计模式。一般可以简化用户创建对象的过程。其次可以降低耦合度,用户不需要关心对象具体的创建过程。包含:单例模式、原型模型、工厂模式、建造者模式。
结构型模型:组织对象之间的结构。使其易于扩展等。包括:代理模式、适配器模式、桥接模式、装饰器模式、外观模式、享元模式、组合模式。
行为模型:主要用于决定对象如何做出行为包括:模板方法模式、策略模式、命令模式、责任链、状态模式、观察者模式、中介者模式、迭代器模式、访问者模式、备忘录模式、解释器模式。
1)提高代码复用率,降低开发成本和周期;
2)提高代码可维护性、可拓展性;
3)使代码更加优雅、更容易被他人理解。
开闭原则要求你的代码对扩展开放,对修改关闭。这个意思就是说,如果你想增加一个新的功 能,你可以很容易的在不改变已测试过的代码的前提下增加新的代码。有好几个设计模式是基 于开闭原则的,如策略模式,如果你需要一个新的策略,只需要实现接口,增加配置,不需要 改变核心逻辑。一个正在工作的例子是 Collections.sort() 方法,这就是基于策略模式,遵循 开闭原则的,你不需为新的对象修改 sort() 方法,你需要做的仅仅是实现你自己的 Comparator 接口。
如果两个对象彼此有关系,就说他们是彼此相关联的。组合和聚合是面向对象中的两种形式的 关联。组合是一种比聚合更强力的关联。组合中,一个对象是另一个的拥有者,而聚合则是指 一个对象使用另一个对象。如果对象 A 是由对象 B 组合的,则 A 不存在的话,B一定不存在, 但是如果 A 对象聚合了一个对象 B,则即使 A 不存在了,B 也可以单独存在。