当前位置: 代码网 > it编程>编程语言>Java > 简单工厂模式在Logback和JDK源码中的应用实例

简单工厂模式在Logback和JDK源码中的应用实例

2026年09月15日 Java 我要评论
创建型模式一、简单工厂模式在logback源码以及jdk源码中的应用1、简单工厂模式简单工厂模式(simple factory pattern):又称为静态工厂方法(static factory me

创建型模式

一、简单工厂模式在logback源码以及jdk源码中的应用

1、简单工厂模式

简单工厂模式(simple factory pattern):又称为静态工厂方法(static factory method)模式,它属于创建型模式。

简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承或实现一个父类或接口)的实例。

1.1 简单工厂模式的uml类图

1.2 日常生活中看简单工厂模式

就拿巴扎黑理发这件事来说,服务员就充当了工厂类,她根据巴扎黑想要剪的价位来安排一位合适的理发师。理发师这个称谓就相当于抽象产品,它描述了所有理发师都具备的能力——理发。服务员安排的这位总监就相当于具体产品。

简单工厂模式中,对于某个具体业务而言,client 无需知道由谁来处理这个业务,怎么处理,它只需告诉工厂类这个业务的类型,并调用工厂类提供的静态方法拿到一个具体的产品,然后调用产品的业务方法来完成业务。就像巴扎黑去餐厅吃饭,他只要告诉服务员他想吃什么,然后自然会有一位师傅帮他做好,自己根本就不需要关心这个师傅是谁以及怎么做出这道美味菜肴。这样看来,吃饭这件事也不麻烦。

但是设想一下,巴扎黑很早就起床了,他想自己做菜来犒劳自己,他有充足的时间来准备。首先他得考虑想要吃什么,于是有了下面的思考:

// 伪代码
preparetoeat(food) {
    if (food == "鱼丸粗面") {
        去买鱼丸和粗面;
        放在锅里烧; 
        ...   
    } else if (food == "大盘鸡") {
        去菜场挑只鸡让大妈杀了,剁好装袋;
        买洋葱和其他配料;
        把鸡肉浸水洗净;
        洋葱切好;
        ...
    } else if (food == "红烧肉") {
        去菜场挑一块五花肉; // 一定要买带肥肉的,不然烧出来很难吃
        切块洗净放料酒浸泡;
        锅里放油煸炒肉块;
        ...
    }
    ...
}

要想吃点好的,脑袋里还得装一本厚厚的菜谱,想要尝尝新菜,还得不断丰富大脑里的菜谱,着实麻烦。

有时候脑袋不够使,还会把菜谱记错,倒不如直接去餐厅点菜吃来的省事儿。

1.3 具体例子

1.3.1 场景

实现一个简单的翻译功能,要求:能够将一段中文文本翻译成不同的语言版本

1.3.2 代码

要实现翻译,我们得有一个翻译器。我们先定义一个抽象的翻译器(abstracttranslator),用于描述所有翻译器都具备的功能——翻译(translate)

/**
 * 翻译器抽象类
 * 抽象产品,定义产品必须实现的方法
 *
 * @author piaolingluo
 * @date 2017-11-08
 */
public abstract class abstracttranslator {

    @autowired
    protected baiduconfig baiduconfig;

    /**
     * 翻译
     *
     * @param content 待翻译的内容
     * @return 翻译的得到的内容
     */
    public abstract string translate(string content);
}

再定义一个具体的翻译器——英语翻译器(englishtranslator),继承abstracttranslator,它能将一段中文翻译成英文

/**
 * 英语翻译器
 * 具体产品
 *
 * @author piaolingluo
 * @date 2017-11-08
 */
@service
public class englishtranslator extends abstracttranslator {

    @autowired
    private gson gson;

    @override
    public string translate(string content) {
        transapi api = new transapi(baiduconfig.getappid(), baiduconfig.getsecuritykey());
        string response = api.gettransresult(content, "auto", "en");
        transresult result = gson.fromjson(response, transresult.class);
        return result.gettransresult().get(0).getdst();
    }
}

再定义一个具体的翻译器——日语翻译器(japanesetranslator),继承abstracttranslator,它能将一段中文翻译成日语

/**
 * 日语翻译器
 * 具体产品
 *
 * @author piaolingluo
 * @date 2017-11-08
 */
@service
public class japanesetranslator extends abstracttranslator {

    @autowired
    private gson gson;

    @override
    public string translate(string content) {
        transapi api = new transapi(baiduconfig.getappid(), baiduconfig.getsecuritykey());
        string response = api.gettransresult(content, "auto", "jp");
        transresult result = gson.fromjson(response, transresult.class);
        return result.gettransresult().get(0).getdst();
    }
}

接着,我们定义系统能能翻译的语言有哪些,如果后续支持新的语言,可以追加

/**
 * 语言枚举
 * 定义系统能翻译的语言
 *
 * @author piaolingluo
 * @date 2017-11-08
 */
@getter
public enum languageenum {

    chinese("chinese", "中文"),

    english("english", "英语"),

    japanese("japanese", "日语");

    private string code;

    private string name;

    languageenum(string code, string name) {
        this.code = code;
        this.name = name;
    }

    public static languageenum valueoflanguage(string code) {
        return stream.of(values())
                .filter(languageenum -> languageenum.getcode().equals(code))
                .findfirst()
                .orelse(null);
    }
}

接着,比较重要的一点是为不同的语言指定各自的实例,后续如果支持新的语言,可以在此扩展

/**
 * 语言类型与翻译器实例的映射
 *
 * @author piaolingluo
 * @date 2017-11-08
 */
@getter
public enum translatorenum {

    english_translator(languageenum.english, "englishtranslator"),

    japanese_translator(languageenum.japanese, "japanesetranslator");

    /**
     * 语言
     */
    private languageenum language;

    /**
     * 具体翻译器处理bean的名字
     */
    private string translatorname;

    translatorenum(languageenum language, string translatorname) {
        this.language = language;
        this.translatorname = translatorname;
    }

    public static translatorenum valueoftranslator(string translatorname) {
        return stream.of(values())
                .filter(translatorenum -> translatorenum.gettranslatorname().equals(translatorname))
                .findfirst()
                .orelse(null);
    }

    public static languageenum languageoftranslator(string translatorname) {
        optional<translatorenum> optional = optional.ofnullable(valueoftranslator(translatorname));
        return optional.ispresent() ? optional.get().getlanguage() : null;
    }
}

最最重要的就是编写这个工厂类(factory),初始化的时候,将所有的翻译器按各自能处理的语言类型存放在 translator_map 里,并提供一个静态方法,能够根据不同的语言拿到具体的翻译器

/**
 * 工厂
 *
 * @author piaolingluo
 * @date 2017-11-08
 */
@component
public class factory {

    private static final map<languageenum, abstracttranslator> translator_map = new hashmap<>();

    @autowired
    private applicationcontext applicationcontext;

    @postconstruct
    public void init() {
        applicationcontext.getbeansoftype(abstracttranslator.class)
                .entryset().stream()
                .filter(entry -> null != translatorenum.valueoftranslator(entry.getkey()))
                .foreach(entry -> translator_map.put(
                        translatorenum.languageoftranslator(entry.getkey()),
                        entry.getvalue()));
    }

    /**
     * 根据语言枚举拿到指定语言的翻译器
     *
     * @param languageenum 语言枚举
     * @return 指定语言的翻译器
     * @throws exception 当拿不到翻译器时,抛出此异常
     */
    public static abstracttranslator gettranslator(languageenum languageenum) throws exception {
        abstracttranslator translator = translator_map.get(languageenum);
        if (null == translator) {
            throw new exception("无法翻译成这种语言");
        }
        return translator;
    }

    /**
     * 根据语言编码拿到指定语言的翻译器
     *
     * @param languagecode 语言编码
     * @return 指定语言的翻译器
     * @throws exception 当拿不到翻译器时,抛出此异常
     */
    public static abstracttranslator gettranslator(string languagecode) throws exception {
        return gettranslator(languageenum.valueoflanguage(languagecode));
    }
}

然后我们写个 translatecontroller ,接收指定的语言编码和待翻译的文本,返回翻译后的文本

/**
 * 翻译服务
 *
 * @author piaolingluo
 * @date 2017-11-08
 */
@restcontroller
@requestmapping("translate")
public class translatecontroller {

    @getmapping("[code]")
    public responseentity<string> translate(@pathvariable("code") string code,
                                    @requestparam(value = "content", required = false) string content) {
        try {
            // 调用工厂类的静态方法,传入语言编码,拿到具体的翻译器实例进行翻译
            return responseentity.ok(factory.gettranslator(code).translate(content));
        } catch (exception e) {
            return responseentity.ok(e.getmessage());
        }
    }
}

最后我们测试一下。

demo完整代码请戳这里

2、简单工厂模式在logback源码中的应用

在大家经常使用的 logback 中,也可以看到 loggerfactory 中有多个重载的方法 getlogger()。

  public static logger getlogger(string name) {
    iloggerfactory iloggerfactory = getiloggerfactory();
    return iloggerfactory.getlogger(name);
  }

  public static logger getlogger(class clazz) {
    return getlogger(clazz.getname());
  }

3、简单工厂模式在jdk源码中的应用

3.1 calendar 类为例简单工厂模式

可以说简单工厂模式在 jdk 源码中无处不在,下面以 calendar 类为例讲解简单工厂模式在 jdk 源码中的应用。calendar 类的 getinstance() 方法源码如下。

public static calendar getinstance() {
    calendar cal = createcalendar(timezone.getdefaultref(), locale.getdefault(locale.category.format));
    cal.sharedzone = true;
    return cal;
}

进入 createcalendar() 方法中,源码如下:

private static calendar createcalendar(timezone zone, locale alocale) {
    calendar cal = null;

    string caltype = alocale.getunicodelocaletype("ca");
    if (caltype == null) {
        // calendar type is not specified.
        // if the specified locale is a thai locale,
        // returns a buddhistcalendar instance.
        if ("th".equals(alocale.getlanguage())
                && ("th".equals(alocale.getcountry()))) {
            cal = new buddhistcalendar(zone, alocale);
        } else {
            cal = new gregoriancalendar(zone, alocale);
        }
    } else if (caltype.equals("japanese")) {
        cal = new japaneseimperialcalendar(zone, alocale);
    } else if (caltype.equals("buddhist")) {
        cal = new buddhistcalendar(zone, alocale);
    } else {
        // unsupported calendar type.
        // use gregorian calendar as a fallback.
        cal = new gregoriancalendar(zone, alocale);
    }

    return cal;
}

calendar 的 uml 类图如下:

3.2 jdbc简单工厂模式

当我们需要mysql数据库的驱动时,我们就传mysql的参数,用oracle的就传相应的参数。在写jdbc的时候,jdk来实现的时候,

class.forname("com.mysql.jdbc.driver");

通过class.forname把mysql的驱动加载进来,那如果写oracle的驱动呢,这里就变成对应的oracle的jdbc的jar包,oracle的driver类,然后调用drivermanager的getconnection方法,

@callersensitive
      public static connection getconnection(string url)
          throws sqlexception {
          java.util.properties info = new java.util.properties();
          return (getconnection(url, info,reflection.getcallerclass()));         
     }

获取对应的数据库连接,jdbc的过程也是非常简单的,

      //  worker method called by the public getconnection() methods.
    private static connection getconnection(
        string url, java.util.properties info, class<?> caller) throws sqlexception {
        /*
         * when callercl is null, we should check the application's
         * (which is invoking this class indirectly)
         * classloader, so that the jdbc driver class outside rt.jar
         * can be loaded from here.
         */
        classloader callercl = caller != null ? caller.getclassloader() : null;
        synchronized(drivermanager.class) {
            // synchronize loading of the correct classloader.
            if (callercl == null) {
                callercl = thread.currentthread().getcontextclassloader();
            }
        }
 
        if(url == null) {
            throw new sqlexception("the url cannot be null", "08001");
        }
 
        println("drivermanager.getconnection(\"" + url + "\")");
 
        // walk through the loaded registereddrivers attempting to make a connection.
        // remember the first exception that gets raised so we can reraise it.
        sqlexception reason = null;
 
        for(driverinfo adriver : registereddrivers) {
            // if the caller does not have permission to load the driver then
            // skip it.
            if(isdriverallowed(adriver.driver, callercl)) {
                try {
                    println("    trying " + adriver.driver.getclass().getname());
                    connection con = adriver.driver.connect(url, info);
                    if (con != null) {
                        // success!
                        println("getconnection returning " + adriver.driver.getclass().getname());
                        return (con);
                    }
                } catch (sqlexception ex) {
                    if (reason == null) {
                        reason = ex;
                    }
                }
 
            } else {
                println("    skipping: " + adriver.getclass().getname());
            }
 
        }
 
        // if we got here nobody could connect.
        if (reason != null)    {
            println("getconnection failed: " + reason);
            throw reason;
        }
 
        println("getconnection: no suitable driver found for "+ url);
        throw new sqlexception("no suitable driver found for "+ url, "08001");
    }

通过class.forname这种方式,直接通过反射拿到对应的video,只不过msyql这里面还是需要通过注册的,

        // walk through the loaded registereddrivers attempting to locate someone
        // who understands the given url.
        for (driverinfo adriver : registereddrivers) {
            // if the caller does not have permission to load the driver then
            // skip it.
            if(isdriverallowed(adriver.driver, callerclass)) {
                try {
                    if(adriver.driver.acceptsurl(url)) {
                        // success!
                        println("getdriver returning " + adriver.driver.getclass().getname());
                    return (adriver.driver);
                    }

                } catch(sqlexception sqe) {
                    // drop through and try the next driver.
                }
            } else {
                println("    skipping: " + adriver.driver.getclass().getname());
            }

        }

 因为这个可以看出来它是一个for循环,在遍历注册的一个驱动, private final static copyonwritearraylist<driverinfo> registereddrivers = new copyonwritearraylist<>(); 并且它是copyonwritearraylist,里面是driverinfo,初始化的时候他是一个空的,具体是什么时候完成注册的呢,

static {  
    try {  
        java.sql.drivermanager.registerdriver(new driver());  
    } catch (sqlexception e) {  
        throw new runtimeexception("can't register driver!");  
    }  
}

这个时候就会在registerdriver(new driver())这个方法里面直接注册这个driver,那里面的driver自然就是mysql的driver,

public static synchronized void registerdriver(java.sql.driver driver,

            driveraction da)

        throws sqlexception {

 

        /* register the driver if it has not already been added to our list */

        if(driver != null) {

            registereddrivers.addifabsent(new driverinfo(driver, da));

        } else {

            // this is for compatibility with the original drivermanager

            throw new nullpointerexception();

        }

 

        println("registerdriver: " + driver);

}

如果不存在就往里放

if(driver != null) {

registereddrivers.addifabsent(new driverinfo(driver, da));

}

4、简单工厂模式的优缺点

4.1 简单工厂模式的优点

工厂类是整个模式的关键,包含了必要的逻辑判断,根据外界给定的信息,决定究竟应该创建哪个具体类的对象。

通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面摆脱出来,仅仅需要负责“消费”对象就可以了。而不必管这些对象究竟如何创建及如何组织的。

明确了各自的职责和权利,有利于整个软件体系结构的优化。

4.2 简单工厂模式的缺点

由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中。

它所能创建的类只能是事先考虑到的,如果需要添加新的类,就需要改变工厂类了。

4.3 简单工厂模式的适用环境

(1) 工厂类负责创建的对象比较少:由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂;

(2) 客户端只知道传入工厂类的参数,对于如何创建对象不关心:客户端既不需要关心创建细节,甚至连类名都不需要记住,只需要知道类型所对应的参数。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com