【设计模式】简单工厂模式

star2017 1年前 ⋅ 826 阅读

简介

定义:定义一个工厂类,可以根据传入的参数不同创建不同类实例,被创建的实例通常都有相同的父类。
简单工厂模式在java中得到了大量的使用,它属于创建型的设计模式,但是它不属于GOF23设计模式中的一种。工厂模式提供公共的接口,客户端直接使用公共接口来创建对象,客户端这边不关心对象是怎么创建的,其中包含3个角色:工厂角色,抽象产品角色,具体产品角色,工厂角色是简单工厂模式的核心,负责产品实例的内部逻辑;抽象产品角色是所有具体产品角色的父类,封装了公共的方法;具体产品角色是工厂角色创建的目标对象。因为简单工厂模式将对象的创建和使用分离,使得系统更加符合单一职责原则。

适用场景

1、工厂类创建的对象比较少
2、客户端只需要传入某个参数,对如何创建对象不关心

优点

1、只需要传入参数就可以获取到需要的对象,客户端使用简单。
2、通过反射或者配置文件,可以在不修改任何代码的情况下更换或者新增产品类,提供系统的灵活性。
3、让创建和使用进行分离。

缺点

1、工厂类的职责比较重,如果新增一些类,需要修改工厂类判断逻辑,违背了开闭原则。
2、增加类的个数,增加系统的复杂性和理解难度。

代码示例

如图,可以看到简单工厂的UML类图:
image.png
富士康可以生产华为、苹果等手机,那么以这个为例子来看看具体的类:

public interface Mobile {
    void produce();
}
public class IphoneMobile implements Mobile{
    public void produce() {
        System.out.println("生产苹果手机");
    }
}
public class HuaweiMobile implements Mobile{
    public void produce() {
        System.out.println("生产华为手机");
    }
}
public class FoxconnFactory {
    public Mobile getMobile(String mobileType){
        if("iphone".equals(mobileType)){
            return new IphoneMobile();
        }else if("huawei".equals(mobileType)){
            return new HuaweiMobile();
        }
        return null;
    }
}
public class SimpleFactoryTest {
    public static void main(String[] args) {
        FoxconnFactory foxconnFactory = new FoxconnFactory();
        Mobile mobile = foxconnFactory.getMobile("iphone");
        mobile.produce();
        Mobile huawei = foxconnFactory.getMobile("huawei");
        huawei.produce();
    }
}

通过传入的mobileType类型来判断创建什么手机,如果需要创建小米,那么需要修改FoxconnFactory里的getMobile方法,这样就会对工厂类进行修改,可以通过反射来修改上面的方法。如:

public Mobile getMobile(Class c){
        try {
            return (Mobile)Class.forName(c.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

 public static void main(String[] args) {
        FoxconnFactory foxconnFactory = new FoxconnFactory();
        Mobile ipohoneMobile = foxconnFactory.getMobile(IphoneMobile.class);
        ipohoneMobile.produce();
    }

改成反射,如果新增一个手机,那么不需要修改工厂类了,符合开闭原则。
上面示例源码

源码分析

日期中使用

Calendar中也有使用简单工厂模式,如:

public static Calendar getInstance(TimeZone zone)
    {
        return createCalendar(zone, Locale.getDefault(Locale.Category.FORMAT));
    }
private static Calendar createCalendar(TimeZone zone,
                                           Locale aLocale)
    {
       //省略.....
        Calendar cal = null;
        if (aLocale.hasExtensions()) {
            String caltype = aLocale.getUnicodeLocaleType("ca");
            if (caltype != null) {
                switch (caltype) {
                case "buddhist":
                cal = new BuddhistCalendar(zone, aLocale);
                    break;
                case "japanese":
                    cal = new JapaneseImperialCalendar(zone, aLocale);
                    break;
                case "gregory":
                    cal = new GregorianCalendar(zone, aLocale);
                    break;
                }
            }
        }
        if (cal == null) {
            //省略.....
            if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
                cal = new BuddhistCalendar(zone, aLocale);
            } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                       && aLocale.getCountry() == "JP") {
                cal = new JapaneseImperialCalendar(zone, aLocale);
            } else {
                cal = new GregorianCalendar(zone, aLocale);
            }
        }
        return cal;
    }

createCalendar中根据Locale来判断使用哪个Calendar,来看下它的UML图:
image.png

DriverManager中使用

DriverManager中的registerDriver方法,也使用了简单工厂,DriverManager.registerDriver(new Driver());通过传入java.sql.Driver来注册具体哪个数据库驱动。

日志中使用

protected Logger logger = LoggerFactory.getLogger(getClass());

通过该代码来获取具体使用哪个日志框架,部分源码:

public static Logger getLogger(Class<?> clazz) {
        Logger logger = getLogger(clazz.getName());
        if (DETECT_LOGGER_NAME_MISMATCH) {
            Class<?> autoComputedCallingClass = Util.getCallingClass();
            if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
                Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(),
                                autoComputedCallingClass.getName()));
                Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
            }
        }
        return logger;
    }
public static Logger getLogger(String name) {
        ILoggerFactory iLoggerFactory = getILoggerFactory();
        return iLoggerFactory.getLogger(name);
    }
public class Log4jLoggerFactory implements ILoggerFactory {
    //省略代码。。。。
    public Logger getLogger(String name) {
        Logger slf4jLogger = loggerMap.get(name);
        if (slf4jLogger != null) {
            return slf4jLogger;
        } else {
            org.apache.log4j.Logger log4jLogger;
            if (name.equalsIgnoreCase(Logger.ROOT_LOGGER_NAME))
                log4jLogger = LogManager.getRootLogger();
            else
                log4jLogger = LogManager.getLogger(name);

            Logger newInstance = new Log4jLoggerAdapter(log4jLogger);
            Logger oldInstance = loggerMap.putIfAbsent(name, newInstance);
            return oldInstance == null ? newInstance : oldInstance;
        }
    }
}

可以看出通过名称来获取日志框架,UML类图如下:
image.png

本文为博主原创文章,未经博主允许不得转载。
更多内容请访问:IT源点

相关文章推荐

全部评论: 0

    我有话说: