从框架源码中学习创建型设计模式

从框架源码中解读创建型设计模式

概念:创建型设计模式顾名思义是用来创建对象的同时隐藏创建逻辑的方式,而不是通过new关键字直接实例化对象,使得程序判别某个对象是否需要创建时更加灵活。

工厂模式

工厂模式是最常见的设计模式。通过工厂封装对象创建逻辑提供一个接口供调用者创建对象。

案例一:RocketMQ源码-创建Producer生产者

public class ProducerFactory {

    public static DefaultMQProducer getRMQProducer(String ns) {
        DefaultMQProducer producer = new DefaultMQProducer(RandomUtil.getStringByUUID());
        producer.setInstanceName(UUID.randomUUID().toString());
        producer.setNamesrvAddr(ns);
        try {
            producer.start();
        } catch (MQClientException e) {
            e.printStackTrace();
        }

        return producer;
    }
}

每调用一次都会获得新的对象实例。

案例二:RocketMQ源码-创建过滤器工厂

public class FilterFactory {

    public static final FilterFactory INSTANCE = new FilterFactory();

    protected static final Map<String, FilterSpi> FILTER_SPI_HOLDER = new HashMap<String, FilterSpi>(4);

    static {
        FilterFactory.INSTANCE.register(new SqlFilter());
    }

    public void register(FilterSpi filterSpi) {
        if (FILTER_SPI_HOLDER.containsKey(filterSpi.ofType())) {
            throw new IllegalArgumentException(String.format("Filter spi type(%s) already exist!", filterSpi.ofType()));
        }

        FILTER_SPI_HOLDER.put(filterSpi.ofType(), filterSpi);
    }

    public FilterSpi unRegister(String type) {
        return FILTER_SPI_HOLDER.remove(type);
    }

    public FilterSpi get(String type) {
        return FILTER_SPI_HOLDER.get(type);
    }

}

没创建完将对象缓存到本地内存中,之后从内存中获取对象

抽象工厂

用来创建工厂的工厂。

案例一:Dubbo源码-创建缓存的抽象工厂

先定义一个创建工厂的接口(@SPI:表示这个接口是基于SPI拓展接口)。

@SPI("lru")
public interface CacheFactory {
    @Adaptive("cache")
    Cache getCache(URL url, Invocation invocation);
}

定义一个抽象工厂实现通用的getCache()方法,并将对象管理逻辑定义在抽象类中并提供一个createCache()方法让工厂的实现类不用关系内部对象管理逻辑只需要实现createCache()方法即可。

public abstract class AbstractCacheFactory implements CacheFactory {
    private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>();
    @Override
    public Cache getCache(URL url, Invocation invocation) {
        url = url.addParameter(METHOD_KEY, invocation.getMethodName());
        String key = url.toFullString();
        Cache cache = caches.get(key);
        if (cache == null) {
            caches.put(key, createCache(url));
            cache = caches.get(key);
        }
        return cache;
    }

    protected abstract Cache createCache(URL url);

}

实现抽象工厂的工厂实现类,缓存有多种实现方式有LRU、LFU等等

public class LruCacheFactory extends AbstractCacheFactory {

    @Override
    protected Cache createCache(URL url) {
        return new LruCache(url);
    }

}
public class LfuCacheFactory extends AbstractCacheFactory {

    @Override
    protected Cache createCache(URL url) {
        return new LfuCache(url);
    }

}

过期时间缓存实现工厂

public class ExpiringCacheFactory extends AbstractCacheFactory {

    @Override
    protected Cache createCache(URL url) {
        return new ExpiringCache(url);
    }
}

案例二:RocketMQ源码-创建日志对象的抽象工厂

public abstract class InternalLoggerFactory {

    public static final String LOGGER_SLF4J = "slf4j";

    public static final String LOGGER_INNER = "inner";

    public static final String DEFAULT_LOGGER = LOGGER_SLF4J;

    private static String loggerType = null;

    //缓存容器
    private static ConcurrentHashMap<String, InternalLoggerFactory> loggerFactoryCache = new ConcurrentHashMap<String, InternalLoggerFactory>();

    //通过类获取对象实例
    public static InternalLogger getLogger(Class clazz) {
        return getLogger(clazz.getName());
    }

    //通过日志类型获取内部日志对象实例
    public static InternalLogger getLogger(String name) {
        return getLoggerFactory().getLoggerInstance(name);
    }

    //获取内部工厂对象
    private static InternalLoggerFactory getLoggerFactory() {
        InternalLoggerFactory internalLoggerFactory = null;
        if (loggerType != null) {
            internalLoggerFactory = loggerFactoryCache.get(loggerType);
        }
        if (internalLoggerFactory == null) {
            internalLoggerFactory = loggerFactoryCache.get(DEFAULT_LOGGER);
        }
        if (internalLoggerFactory == null) {
            internalLoggerFactory = loggerFactoryCache.get(LOGGER_INNER);
        }
        if (internalLoggerFactory == null) {
            throw new RuntimeException("[RocketMQ] Logger init failed, please check logger");
        }
        return internalLoggerFactory;
    }

    //设置当前日志类型
    public static void setCurrentLoggerType(String type) {
        loggerType = type;
    }

    //程序启动默认注册Slf4j工厂和内部日志工厂到loggerFactoryCache容器中
    static {
        try {
            new Slf4jLoggerFactory();
        } catch (Throwable e) {
            //ignore
        }
        try {
            new InnerLoggerFactory();
        } catch (Throwable e) {
            //ignore
        }
    }

    //注册工厂逻辑
    protected void doRegister() {
        String loggerType = getLoggerType();
        if (loggerFactoryCache.get(loggerType) != null) {
            return;
        }
        loggerFactoryCache.put(loggerType, this);
    }

    protected abstract void shutdown();

    //获取内部日志对象
    protected abstract InternalLogger getLoggerInstance(String name);

    //获取日志类型
    protected abstract String getLoggerType();
}

通过抽象工厂实现Slf4jLoggerFactory工厂

public class Slf4jLoggerFactory extends InternalLoggerFactory {

    public Slf4jLoggerFactory() {
        LoggerFactory.getILoggerFactory();
        doRegister();
    }

    @Override
    protected String getLoggerType() {
        return InternalLoggerFactory.LOGGER_SLF4J;
    }

    @Override
    protected InternalLogger getLoggerInstance(String name) {
        return new Slf4jLogger(name);
    }

    @Override
    protected void shutdown() {

    }

    public static class Slf4jLogger implements InternalLogger {

        private Logger logger = null;

        public Slf4jLogger(String name) {
            logger = LoggerFactory.getLogger(name);
        }

        @Override
        public String getName() {
            return logger.getName();
        }

        @Override
        public void debug(String s) {
            logger.debug(s);
        }

        @Override
        public void info(String s) {
            logger.info(s);
        }

        @Override
        public void warn(String s) {
            logger.warn(s);
        }

        @Override
        public void warn(String s, Throwable throwable) {
            logger.warn(s, throwable);
        }

        @Override
        public void error(String s) {
            logger.error(s);
        }

        @Override
        public void error(String s, Throwable throwable) {
            logger.error(s, throwable);
        }
    }
}

单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。该类负责创建自己的对象,同时确保只有单个对象被创建,同时提供了一种访问其唯一对象的方式。

面试官:单例有几种写法?

  1. 饿汉式:类加载时就初始化,浪费内存
  2. 懒汉式-非线程安全:用到的时候才初始化
  3. 懒汉式-线程安全synchronized方法,每次都需要加载性能慢
  4. 懒汉式-双重检查锁,这种方式比第三种性能更高,但是每次都需要做判断,而且书写麻烦个人建议还不如用静态内部类方式
  5. 枚举:这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。推荐使用
  6. 静态内部类:在主类中提供一个静态内部类在初始化时候创建对象,主类提供获取单例对象方法,用到的时候初始化,通过ClassLoader机制保证只有一个线程创建实例。推荐使用

案例一:dubbo源码-饿汉式

public class ShutdownHookCallbacks {

    public static final ShutdownHookCallbacks INSTANCE = new ShutdownHookCallbacks();

    private final List<ShutdownHookCallback> callbacks = new LinkedList<>();

    ShutdownHookCallbacks() {
        loadCallbacks();
    }

    public ShutdownHookCallbacks addCallback(ShutdownHookCallback callback) {
        synchronized (this) {
            this.callbacks.add(callback);
        }
        return this;
    }

    public Collection<ShutdownHookCallback> getCallbacks() {
        synchronized (this) {
            sort(this.callbacks);
            return this.callbacks;
        }
    }

    public void clear() {
        synchronized (this) {
            callbacks.clear();
        }
    }

    private void loadCallbacks() {
        ExtensionLoader<ShutdownHookCallback> loader =
                ExtensionLoader.getExtensionLoader(ShutdownHookCallback.class);
        loader.getSupportedExtensionInstances().forEach(this::addCallback);
    }

    public void callback() {
        getCallbacks().forEach(callback -> execute(callback::callback));
    }
}

案例二:RocketMQ源码-懒汉式-非线程安全

public class MQClientManager {
    private final static InternalLogger log = ClientLogger.getLog();
    private static MQClientManager instance = new MQClientManager();
    private AtomicInteger factoryIndexGenerator = new AtomicInteger();
    private ConcurrentMap<String/* clientId */, MQClientInstance> factoryTable =
        new ConcurrentHashMap<String, MQClientInstance>();

    private MQClientManager() {

    }
    
    public static MQClientManager getInstance() {
        return instance;
    }

    public MQClientInstance getOrCreateMQClientInstance(final ClientConfig clientConfig) {
        return getOrCreateMQClientInstance(clientConfig, null);
    }

    //懒汉式
    public MQClientInstance getOrCreateMQClientInstance(final ClientConfig clientConfig, RPCHook rpcHook) {
        String clientId = clientConfig.buildMQClientId();
        MQClientInstance instance = this.factoryTable.get(clientId);
        if (null == instance) {
            instance =
                new MQClientInstance(clientConfig.cloneClientConfig(),
                    this.factoryIndexGenerator.getAndIncrement(), clientId, rpcHook);
            MQClientInstance prev = this.factoryTable.putIfAbsent(clientId, instance);
            if (prev != null) {
                instance = prev;
                log.warn("Returned Previous MQClientInstance for clientId:[{}]", clientId);
            } else {
                log.info("Created new MQClientInstance for clientId:[{}]", clientId);
            }
        }

        return instance;
    }

    public void removeClientFactory(final String clientId) {
        this.factoryTable.remove(clientId);
    }
}

特别注意:使用这种方式获取单例非线程安全的,那RocketMQ这样使用不是有错呢?如果只是单纯这样使用肯定是有错的,但是上层调用加了synchronized就没有问题,如下

public class DefaultMQPullConsumerImpl implements MQConsumerInner {
  ...
    public synchronized void start() throws MQClientException {
        switch (this.serviceState) {
            case CREATE_JUST:
                this.serviceState = ServiceState.START_FAILED;

                this.checkConfig();

                this.copySubscription();

                if (this.defaultMQPullConsumer.getMessageModel() == MessageModel.CLUSTERING) {
                    this.defaultMQPullConsumer.changeInstanceNameToPID();
                }

                this.mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(this.defaultMQPullConsumer, this.rpcHook);
            ...
}

案例三:双重检查锁

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
}

案例四:线程安全synchronized方法

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}

案例五:枚举类

public enum Singleton {

    INSTANCE;

    public void doSomething() {
        System.out.println("doSomething");
    }

}

案例六:静态内部类

public class Singleton {  
    private static class SingletonHolder {  
    private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
    return SingletonHolder.INSTANCE;  
    }  
}

建造者模式

使用多个简单的对象一步步构成复制对象。

案例:dubbo源码使用

package org.apache.dubbo.config.bootstrap.builders;

import org.apache.dubbo.config.RegistryConfig;

import java.util.Map;

/**
 * This is a builder for build {@link RegistryConfig}.
 *
 * @since 2.7
 */
public class RegistryBuilder extends AbstractBuilder<RegistryConfig, RegistryBuilder> {
    /**
     * Register center address
     */
    private String address;

    /**
     * Username to login register center
     */
    private String username;

    /**
     * Password to login register center
     */
    private String password;

    /**
     * Default port for register center
     */
    private Integer port;

    /**
     * Protocol for register center
     */
    private String protocol;

    /**
     * Network transmission type
     */
    private String transporter;

    private String server;

    private String client;

    private String cluster;

  	...

    public static RegistryBuilder newBuilder() {
        return new RegistryBuilder();
    }

    public RegistryBuilder id(String id) {
        return super.id(id);
    }

    public RegistryBuilder address(String address) {
        this.address = address;
        return getThis();
    }

    public RegistryBuilder username(String username) {
        this.username = username;
        return getThis();
    }

    public RegistryBuilder password(String password) {
        this.password = password;
        return getThis();
    }
    ...

    public RegistryConfig build() {
        RegistryConfig registry = new RegistryConfig();
        super.build(registry);

        registry.setCheck(check);
        registry.setClient(client);
        registry.setCluster(cluster);
        registry.setDefault(isDefault);
        registry.setDynamic(dynamic);
        registry.setExtraKeys(extraKeys);
        registry.setFile(file);
        registry.setGroup(group);
        registry.setParameters(parameters);
        registry.setPassword(password);
        registry.setPort(port);
        registry.setProtocol(protocol);
        registry.setRegister(register);
        registry.setServer(server);
        registry.setSession(session);
        registry.setSimplified(simplified);
        registry.setSubscribe(subscribe);
        registry.setTimeout(timeout);
        registry.setTransporter(transporter);
        registry.setUsername(username);
        registry.setVersion(version);
        registry.setWait(wait);
        registry.setUseAsConfigCenter(useAsConfigCenter);
        registry.setUseAsMetadataCenter(useAsMetadataCenter);
        registry.setAccepts(accepts);
        registry.setPreferred(preferred);
        registry.setWeight(weight);
        registry.setAddress(address);

        return registry;
    }

    @Override
    protected RegistryBuilder getThis() {
        return this;
    }
}

原型模式

用来拷贝对象,通过实现Cloneable接口中的clone()方法,使用时注意深拷贝、浅拷贝问题。

案例:RocketMQ源码-拷贝数组对象

package org.apache.rocketmq.filter.util;

/**
 * Wrapper of bytes array, in order to operate single bit easily.
 */
public class BitsArray implements Cloneable {

    private byte[] bytes;
    private int bitLength;

    public static BitsArray create(int bitLength) {
        return new BitsArray(bitLength);
    }

    private BitsArray(int bitLength) {
        this.bitLength = bitLength;
        // init bytes
        int temp = bitLength / Byte.SIZE;
        if (bitLength % Byte.SIZE > 0) {
            temp++;
        }
        bytes = new byte[temp];
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = (byte) 0x00;
        }
    }


    private BitsArray(byte[] bytes) {
        if (bytes == null || bytes.length < 1) {
            throw new IllegalArgumentException("Bytes is empty!");
        }

        this.bitLength = bytes.length * Byte.SIZE;
        this.bytes = new byte[bytes.length];
        System.arraycopy(bytes, 0, this.bytes, 0, this.bytes.length);
    }

  	...

    public BitsArray clone() {
        byte[] clone = new byte[this.byteLength()];

        System.arraycopy(this.bytes, 0, clone, 0, this.byteLength());

        return create(clone, bitLength());
    }
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值