设计模式之工厂模式

工厂模式

先来看一个案例

需求:实现一个咖啡点餐功能。

类图:
在这里插入图片描述

主函数方法

public class App {
    public static void main(String[] args) {
        CoffeeStore coffeeStore = new CoffeeStore();
        Coffee coffee = coffeeStore.orderCoffee("AmericanCoffee");
        System.out.println(coffee.getName());
        // 输出:
        // add mike...
	    // add sugar...
        // AmericanCoffee
    }
}

CoffeeStore

public class CoffeeStore {
    // 点咖啡
    public Coffee orderCoffee(String type) {
        Coffee coffee = null;
        if ("AmericanCoffee".equals(type)) {
            coffee = new AmericanCoffee();
        } else if ("LatteCoffee".equals(type)) {
            coffee = new LatteCoffee();
        } else {
            throw new RuntimeException("对不起, 本店没有您所点的咖啡");
        }
        coffee.addMike();
        coffee.addSugar();
        return coffee;
    }
}

Coffee

public abstract class Coffee {

    public abstract String getName();

    public void addMike() {
        System.out.println("add mike...");
    }

    public void addSugar() {
        System.out.println("add sugar...");
    }

}

AmericanCoffee

public class AmericanCoffee extends Coffee {
    @Override
    public String getName() {
        return "AmericanCoffee";
    }
}

LatteCoffee

public class LatteCoffee extends Coffee {
    @Override
    public String getName() {
        return "LatteCoffee";
    }
}

上述案例中存在的问题?
如果此时需要新增一种咖啡品种,那么我们就需要去修改CoffeeStore中的代码,与Coffee实现类严重耦合,违反了设计原则中的开闭原则

开闭原则: 对扩展打开,对修改关闭。也就是说在有新的需求时,只通过扩展代码来满足,而不是去修改代码。

简单工厂模式

类图:
在这里插入图片描述

在原有的代码上新增一个工厂类SimpleCoffeeFactory,将创建咖啡的工作完全交给这个工厂类。

public class SimpleCoffeeFactory {
	// 根据咖啡类型创建咖啡 
    public Coffee createCoffee(String type) {
        Coffee coffee = null;
        if ("AmericanCoffee".equals(type)) {
            coffee = new AmericanCoffee();
        } else if ("LatteCoffee".equals(type)) {
            coffee = new LatteCoffee();
        } else {
            throw new RuntimeException("对不起, 本店没有您所点的咖啡");
        }
        coffee.addMike();
        coffee.addSugar();
        return coffee;
    }
}

修改CoffeeStore

public class CoffeeStore {
    // 点咖啡
    public Coffee orderCoffee(String type) {
        SimpleCoffeeFactory simpleCoffeeFactory = new SimpleCoffeeFactory();
        Coffee coffee = simpleCoffeeFactory.createCoffee(type);
        return coffee;
    }
}

这样做虽然解除了CoffeeStore与Coffee实现类之间的耦合,但是又新增了耦合关系,比如我们在新增咖啡品种时,还是要去修改工厂类中的方法,导致工厂类与Coffe实现类耦合了,这种方式也违反了开闭原则,因此,23中设计模式中不包括简单工厂模式。

这种方式在实际中还是会经常被用到的,它是有一定的好处的,想象一下,当有多个CoffeeStore出现时,或者举个例子,出现了一个DessertStore甜品店,它也需要点咖啡的功能,如果没有工厂,我们还需要在DessertStore中去写一大堆的if else代码去创建咖啡,不如将创建咖啡的工作完全交给一个工厂类,这样一来,不管出现多少个这种店,需要咖啡对象我们直接从工厂从拿就可以了。

工厂模式

正儿八经的工厂模式来了。

类图:
在这里插入图片描述

在原有代码基础上,做以下操作:

创建CoffeeFactoty接口

public interface CoffeeFactory {
    Coffee createCoffee();
}

创建AmericanCoffeeFactory实现CoffeeFactory

public class AmericanCoffeeFactory implements CoffeeFactory {
    @Override
    public Coffee createCoffee() {
        return new AmericanCoffee();
    }
}

创建LatteCoffeeFactory实现CoffeeFactory

public class LatteCoffeeFactory implements CoffeeFactory {
    @Override
    public Coffee createCoffee() {
        return new LatteCoffee();
    }
}

修改CoffeeStore

public class CoffeeStore {
    // 点咖啡
    public Coffee orderCoffee(CoffeeFactory coffeeFactory) {
        Coffee coffee = coffeeFactory.createCoffee();
        coffee.addMike();
        coffee.addSugar();
        return coffee;
    }
}

主函数方法

public class App {
    public static void main(String[] args) {
        CoffeeStore coffeeStore = new CoffeeStore();
        Coffee coffee = coffeeStore.orderCoffee(new AmericanCoffeeFactory());
        System.out.println(coffee.getName());
    }
}

优点:

  • 我们只需要知道具体工厂类的名称就可以拿到其返回的对象,而不需要关注具体的创建过程;
  • 在有新的咖啡品种出现时,我们只需要创建对应的咖啡品种类和其对应的工厂类即可,满足开闭原则

缺点:

  • 每次有新的咖啡品种出现,我们都需要创建其实体与工厂类,随着需求越来越多,这种方式会极大增加系统的复杂性。

抽象工厂模式

在普通工厂模式中,我们发现它只针对一个产品类型 ,比如上述案例中,咖啡工厂就只能去生产咖啡。

在实际应用中往往有一些别的需求,比如在甜品工厂中需要生产咖啡和甜品,而咖啡又分为美式咖啡和意式咖啡,甜品又分为巧克力蛋糕、奶酪等;在比如,电子产品工厂需要制造手机和电脑,而手机又分为华为手机和苹果手机,电脑也分为华为电脑和苹果电脑,这个时候就出现了抽象工厂模式。

工厂模式是只针对一个产品等级,而抽象产品模式针对多个产品等级。

产品族: 比如华为品牌下有华为手机、华为电脑、手表等,它们称为一个产品族。
产品等级: 多个品牌下的同类产品,比如华为手机、小米手机等等,手机就称为一个产品等级。

下面用上述电子产品的例子来实现案例。

准备工作

public abstract class Computer {
    public abstract String getName();
}
public class HuaWeiComputer extends Computer {
    @Override
    public String getName() {
        return "华为电脑";
    }
}
public class AppleComputer extends Computer {
    @Override
    public String getName() {
        return "苹果电脑";
    }
}
public abstract class Phone {
    public abstract String getName();
}
public class ApplePhone extends Phone {
    @Override
    public String getName() {
        return "苹果手机";
    }
}
public class HuaWeiPhone extends Phone {
    @Override
    public String getName() {
        return "华为手机";
    }
}

创建ElectronicProductFactory,超级工厂

public interface ElectronicProductFactory {
    Phone createPhone();
    Computer createComputer();
}

HuaWeiBrandFactory

public class HuaWeiBrandFactory implements ElectronicProductFactory {
    @Override
    public Phone createPhone() {
        return new HuaWeiPhone();
    }

    @Override
    public Computer createComputer() {
        return new HuaWeiComputer();
    }
}

AppleBrandFactory

public class AppleBrandFactory implements ElectronicProductFactory {
    @Override
    public Phone createPhone() {
        return new ApplePhone();
    }

    @Override
    public Computer createComputer() {
        return new AppleComputer();
    }
}

主函数

public class App {
    public static void main(String[] args) {
        HuaWeiBrandFactory huaWeiBrandFactory = new HuaWeiBrandFactory();
        Phone phone = huaWeiBrandFactory.createPhone();
        Computer computer = huaWeiBrandFactory.createComputer();
        System.out.println(phone.getName());	// 华为手机
        System.out.println(computer.getName());	// 华为电脑
    }
}

工厂模式 + 配置文件

这种方式是在工厂类中加载配置文件,读取其中的全类名,再通过反射 的方式来创建其实例,有效的解除了原工厂模式中工厂类和其对应的产品对象的耦合。

AmericanCoffee、Coffee、LatteCoffee这些基础的类不变。

创建properties配置文件

american=com.sunao.factory.ultimate.AmericanCoffee
latte=com.sunao.factory.ultimate.LatteCoffee

创建CoffeeFactory

public class CoffeeFactory {
    public static HashMap<String, Coffee> map = new HashMap<>();

    // 加载配置文件,只需要加载一次,所以用静态代码块来完成
    static {
        Properties properties = new Properties();
        InputStream is = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");
        try {
            properties.load(is);
            // 获取配置文件中的键名
            Set<Object> keys = properties.keySet();
            for (Object key : keys) {
                // 获取对应key的value:全类名
                String className = properties.getProperty((String) key);
                // 通过反射来创建该类实例
                Coffee coffee = (Coffee) Class.forName(className).newInstance();
                map.put((String) key, coffee);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Coffee createCoffee(String name) {
        return map.get(name);
    }
}

主函数方法

public class App {
    public static void main(String[] args) {
        Coffee americanCoffee = CoffeeFactory.createCoffee("american");
        System.out.println(americanCoffee.getName());   // AmericanCoffee

        Coffee latteCoffee = CoffeeFactory.createCoffee("latte");
        System.out.println(latteCoffee.getName());  // LatteCoffee
    
        Coffee americanCoffee02 = CoffeeFactory.createCoffee("american");
        System.out.println(americanCoffee == americanCoffee02); // true
    }
}

再去对比以下我们上述原来的共产模式,当我们需要新添加caffee品种时,还需要创建一大堆对应的实体和其工厂类吗?现在你只需要:

  • 创建新添加品种的实体;
  • 在配置文件中添加其全类名;
  • 工厂类中的代码完全不需要变动,即可获取其实例。

从上述结果中我们发现,在多次获取同一个对象实例时,获取到的是同一个对象,这是因为通过静态代码块来加载配置文件,该配置文件只被加载了一次,从而实现了单例模式。是不是联想到了Spring中的IOC?

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值