學習設計模式系列之“簡單工廠模式”

參考文章1:http://wxg6203.iteye.com/blog/740229
參考文章2:http://blog.csdn.net/zhengzhb/article/details/7348707



學習設計模式系列之“簡單工廠模式”

簡單工廠模式(simple factory pattern)是類的創建模式,又叫靜態工廠方法(static factory method)模式。簡單工廠模式就是由一個工廠類根據傳入的參數決定創建哪一種的產品類。


工廠模式的角色

簡單工廠模式共有4個角色

  • 工廠類角色:是具體產品類角色直接調用者。
  • 抽象產品角色:接口或抽象類,負責具體產品角色的定義,及與客戶端的交互。
  • 具體產品角色:被工廠類創建的對象,也是客戶端實際操作對象。
  • 客戶端:調用工廠類產生實例,並調用實例的方法進行相應工作。

Java代碼實現

抽象產品角色

抽象產品角色是簡單工廠模式所創建的所有對象的父類,它負責描述所有實例所共有的公共接口。

/**
 * 動物類接口
 */
public interface AnimalInterface
{
    // 動物的動作
    public void act();
}

具體產品角色

具體產品角色是簡單工廠模式的創建目標,所有創建的對象都是充當這個角色的某個具體類的實例。

/**
 * 狗
 */
public class Dog implements AnimalInterface
{
    @Override
    public void act()
    {
        System.out.println("我是一隻狗,汪~……");
    }
}

/**
 * 貓
 */
public class Cat implements AnimalInterface
{
    @Override
    public void act()
    {
        System.out.println("我是一隻貓,喵~……");
    }
}

/**
 * 羊
 */
public class Sheep implements AnimalInterface
{
    @Override
    public void act()
    {
        System.out.println("我是一隻羊,咩~……");
    }
}

/**
 * 牛
 */
public class Bull implements AnimalInterface
{
    @Override
    public void act()
    {
        System.out.println("我是一頭牛,哞~……");
    }
}

工廠類角色

工廠類角色是簡單工廠模式的核心,它負責實現創建所有實例的內部邏輯。工廠類的創建產品類的方法可以被外界直接調用,創建所需的產品對象。

實現

**
 * 創建動物對象
 */
public class AnimalFactory
{
    /**
     * 根據字符串創建對象 
     * 缺點:擴展需增加判斷
     * @param key
     * @return
     */
    public static AnimalInterface getAnimalByKey(String key)
    {
        // 注意 "dog".equals(key) "dog"放在前面的用法的好處,避免key爲null的判斷
        if ("dog".equals(key))
        {
            return new Dog();
        }
        else if ("cat".equals(key))
        {
            return new Cat();
        }
        else if ("sheep".equals(key))
        {
            return new Sheep();
        }
        else if ("Bull".equals(key))
        {
            return new Bull();
        }
        return null;
    }


    /**
     * 使用java的反射,根據類名創建對象
     * 缺點:類名太長
     * @param className
     * @return
     */
    public static AnimalInterface getAnimalByClass(String className)
    {
        try
        {
            // 使用反射,動態加載類,創建類對象
            AnimalInterface animal = (AnimalInterface)Class.forName(className).newInstance();
            return animal;
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        return null;
    }


    /**
     * 根據類名的映射來縮寫完整類名
     * @param classKey 類名的簡寫
     * @return
     */
    public static AnimalInterface getAnimalByClassKey(String classKey)
    {
        // 讀properties文件,獲取完整類名
        PropertiesReader reader = new PropertiesReader();
        Map<String, String> animalMap = reader.readProperties("input.properties");
        String className = animalMap.get(classKey);
        // 使用反射,動態加載類,創建類對象
        try
        {
            AnimalInterface animal = (AnimalInterface)Class.forName(className).newInstance();
            return animal;
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        return null;
    }
}

Properties文件

dog=com.factory.dp.Dog
cat=com.factory.dp.Cat
sheep=com.factory.dp.Sheep
bull=com.factory.dp.Bull

讀取Properties文件

/**
 * 讀取properties文件的內容
 */

public class PropertiesReader
{
    /**
     * 根據properties文件名讀取文件內容
     * @return 類簡寫和類的映射
     */
    public Map<String, String> readProperties(String file)
    {
        // 將文件內容讀進map
        Map<String, String> animalMap = new HashMap<String, String>();
        // 創建Properties對象
        Properties p = new Properties();
        InputStream in = getClass().getResourceAsStream(file);
        try
        {
            // 加載輸入流
            p.load(in);
            Enumeration e = p.propertyNames();
            while (e.hasMoreElements())
            {
                String key = (String)e.nextElement();
                String className = p.getProperty(key);
                animalMap.put(key, className);
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }

        return animalMap;
    }
}

客戶端

public class Client
{

    /**
     * 客戶端調用
     */
    public static void main(String[] args)
    {
        // 使用key調用
        AnimalInterface a1 = AnimalFactory.getAnimalByKey("dog");
        a1.act();
        AnimalInterface a2 = AnimalFactory.getAnimalByKey("cat");
        a2.act();

        // 使用類名調用
        AnimalInterface a3 = AnimalFactory.getAnimalByClass("com.factory.dp.Bull");
        a3.act();
        AnimalInterface a4 = AnimalFactory.getAnimalByClass("com.factory.dp.Sheep");
        a4.act();

        // 使用類名的映射來調用
        AnimalInterface a5 = AnimalFactory.getAnimalByClassKey("sheep");
        a5.act();
        AnimalInterface a6 = AnimalFactory.getAnimalByClassKey("bull");
        a6.act();
    }
}

優點

工廠類是整個模式的關鍵.包含了必要的邏輯判斷,根據外界給定的信息,決定究竟應該創建哪個具體類的對象.通過使用工廠類,外界可以從直接創建具體產品對象的尷尬局面擺脫出來,僅僅需要負責“消費”對象就可以了。而不必管這些對象究竟如何創建及如何組織的.明確了各自的職責和權利,有利於整個軟件體系結構的優化。

缺點

由於工廠類集中了所有實例的創建邏輯,違反了高內聚責任分配原則,將全部創建邏輯集中到了一個工廠類中;它所能創建的類只能是事先考慮到的,如果需要添加新的類,則就需要改變工廠類了。
當系統中的具體產品類不斷增多時候,可能會出現要求工廠類根據不同條件創建不同實例的需求.這種對條件的判斷和對具體產品類型的判斷交錯在一起,很難避免模塊功能的蔓延,對系統的維護和擴展非常不利
這些缺點在工廠方法模式中得到了一定的克服。

使用場景

  • 工廠類負責創建的對象比較少;
  • 客戶只知道傳入工廠類的參數,對於如何創建對象(邏輯)不關心;
  • 由於簡單工廠很容易違反高內聚責任分配原則,因此一般只在很簡單的情況下應用。

PS

在工廠類角色創建具體類對象時,使用三種方法:

  • 使用key調用
  • 使用完整類名
  • 使用完整類名的簡寫映射

其中使用反射和Properties文件,對於第一種方法恰恰正是上文中缺點所說的,第二種方法和第三種則進行了改進。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章