工厂模式
在获取对象时我们常使用new,这样对对象的耦合十分严重,如果我们使用工厂来生产对象,我们就只和工厂打交道就可以了,彻底和对象解耦,如果要更换对象,直接在工厂里更换该对象即可,达到了与对象解耦的目的
集合的迭代器就是使用的工厂模式
工厂模式的特点
解耦
工厂模式的类型
- 简单工厂模式(不属于coF的23种经典设计模式)
- 工厂方法模式
- 抽象工厂模式
简单工厂模式
并非一种设计模式而是一种编程习惯
简单工厂包含如下角色:
- 抽象产品︰定义了产品的规范,描述了产品的主要特性和功能。
- 具体产品︰实现或者继承抽象产品的子类
- 具体工厂︰提供了创建产品的方法,调用者通过该方法来创建产品。
简单工厂的优点
封装了创建对象的过程,可以通过参数直接获取对象。把对象的创建和业务逻辑层分开,这样以后就避免了修改客户代码,如果要实现新产品直接修改工厂类,而不需要在原代码中修改,这样就降低了客户代码修改的可能性,更加容易扩展
简单工厂的缺点
增加新产品时还是需要修改工厂类的代码,依然违背了"开闭原则"
示例
Milk
package builder_pattern.factory;
/**
* 抽象产品
*/
public class Milk {
protected String name;
}
GuangMinMilk
package builder_pattern.factory;
/**
* 具体产品
*/
public class GuangMinMilk extends Milk{
public GuangMinMilk() {
this.name = "GuangMin";
}
}
Shop
package builder_pattern.factory;
/**
* 商店,存储,售卖产品
*/
public class Shop {
public Milk sellMilk(String milkName){
//调用工厂生产
MilkFactory milkFactory = new MilkFactory();
Milk milk = milkFactory.createMilk(milkName);
return milk;
}
}
MilkFactory
package builder_pattern.factory;
/**
* 具体工厂
*/
public class MilkFactory {
public Milk createMilk(String milkName){
Milk milk = null;
if (milkName.equals("GuangMin")){
milk =new GuangMinMilk();
}else{
throw new RuntimeException("this type of milk can not be create in this factory");
}
return milk;
}
}
Client
package builder_pattern.factory;
/**
* 具体工厂
*/
public class MilkFactory {
public Milk createMilk(String milkName){
Milk milk = null;
if (milkName.equals("GuangMin")){
milk =new GuangMinMilk();
}else{
throw new RuntimeException("this type of milk can not be create in this factory");
}
return milk;
}
}
结果
解读
简单工厂虽然处理了产品和超市之间的耦合,但是又产生了产品和工厂之间的耦合,也就违反了开闭原则,没能实现完全在原先的基础上扩展功能而不更改代码
静态工厂
开发中有一部分人将工厂类中的创建对象的功能定义为静态,这就是静态工厂模式,属于简单工厂模式
仅需将工厂类中的方法变为静态即可(这样就可以无需new对象)
package builder_pattern.factory;
public class StaticMilkFactory {
public static Milk createMilk(String milkName){
Milk milk = null;
if (milkName.equals("GuangMin")){
milk =new GuangMinMilk();
}else{
throw new RuntimeException("this type of milk can not be create in this factory");
}
return milk;
}
}
工厂方法模式
工厂方法模式完全遵守开闭原则
定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象,工厂方法使一个产品类的实例化延迟到其工厂的子类
在工厂方法中包含:
- 抽象工厂:提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品
- 具体工厂:主要是实现抽象工厂中的抽象方法,完成具体产品的创建
- 抽象产品:定义了产品的规范,描述了产品的主要特性和功。
- 具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应
工厂方法的优点
- 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程
- 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则
工厂方法的缺点
每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度
示例
思想:
- 用户找到商店进行购买
- 商店设置工厂进行生产(商店需指定总工厂下的具体工厂进行生产)
- 具体工厂收到总工厂指令生产具体的产品
- 商店最后才能销售
Milk
package builder_pattern.factory_method;
/**
* 抽象产品
*/
public class Milk {
protected String name;
}
GuangMinMilk
package builder_pattern.factory_method;
/**
* 具体产品
*/
public class GuangMinMilk extends Milk {
public GuangMinMilk() {
this.name = "GuangMin";
}
}
Shop
package builder_pattern.factory_method;
/**
* 商店,存储,售卖产品
*/
public class Shop {
private MilkFactory milkFactory;
public void setMilkFactory(MilkFactory milkFactory) {
this.milkFactory = milkFactory;
}
public Milk sellMilk(){
return this.milkFactory.createMilk();
}
}
MilkFactory
package builder_pattern.factory_method;
/**
* 抽线工厂
*/
public interface MilkFactory {
Milk createMilk();
}
GuangMinMilkFactory
package builder_pattern.factory_method;
/**
* 具体工厂
*/
public class GuangMinMilkFactory implements MilkFactory {
@Override
public Milk createMilk(){
return new GuangMinMilk();
}
}
Client
package builder_pattern.factory_method;
public class Client {
public static void main(String[] args) {
Shop shop = new Shop();
shop.setMilkFactory(new GuangMinMilkFactory());
Milk milk = shop.sellMilk();
System.out.println(milk.name);
}
}
抽象工厂模式
抽象工厂模式将考虑多等级产品的生产,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族,下图所示横轴是产品等级,也就是同一类产品,纵轴是产品族,也就是同一品牌的产品,同一品牌的产品产自同一个工厂
是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。
主要角色:
- 抽象工厂:提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品。
- 具体工厂︰主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
- 抽象产品:定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
- 具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
产品级别
同类型产品,比如:苹果手机,华为手机,小米手机,属于一个等级
产品族
同厂家生产的产品,如:小米手机,小米手环,小米电脑属于一个产品族
抽象工厂优点
当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象
抽象工厂缺点
当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改
使用场景
- 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机等
- 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋
- 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。
如:输入法换皮肤,一整套一起换。生成不同操作系统的程序
示例
Milk
package builder_pattern.abstruct_factory;
/**
* 抽象产品
*/
public class Milk {
protected String name;
}
GuangMinMilk
package builder_pattern.abstruct_factory;
/**
* 具体产品
*/
public class GuangMinMilk extends Milk {
public GuangMinMilk() {
this.name = "光明牛奶";
}
}
Yogurt
package builder_pattern.abstruct_factory;
public class Yogurt {
protected String name;
}
GuangMinYogurt
package builder_pattern.abstruct_factory;
public class GuangMinYogurt extends Yogurt{
public GuangMinYogurt() {
this.name = "光明酸奶";
}
}
MilkFactory
package builder_pattern.abstruct_factory;
/**
* 抽线工厂
*/
public interface MilkFactory {
Milk createMilk();
Yogurt createYogurt();
}
GuangMinMilkFactory
package builder_pattern.abstruct_factory;
/**
* 具体工厂
*/
public class GuangMinMilkFactory implements MilkFactory {
@Override
public Milk createMilk(){
return new GuangMinMilk();
}
@Override
public Yogurt createYogurt() {
return new GuangMinYogurt();
}
}
Client
package builder_pattern.abstruct_factory;
public class Client {
public static void main(String[] args) {
GuangMinMilkFactory guangMinMilkFactory = new GuangMinMilkFactory();
Milk milk = guangMinMilkFactory.createMilk();
Yogurt yogurt = guangMinMilkFactory.createYogurt();
System.out.println(yogurt.name);
System.out.println(milk.name);
}
}
结果
简单工厂+配置文件解除耦合
可以通过工厂模式+配置文件的方式解除工厂对象和产品对象的耦合。在工厂类中加载配置文件中的全类名,并创建对象进行存储,客户端如果需要对象,直接进行获取即可。
目录
1.定义配置文件
为了演示方便,我们使用properties文件作为配置文件,名称为bean.properties
guangMinMilk=example.GuangMinMilk
guangMinYogurt=example.GuangMinYogurt
2.编写类
Milk
package example;
/**
* 抽象产品
*/
public class Milk {
protected String name;
}
GuangMinMilk
package example;
/**
* 具体产品
*/
public class GuangMinMilk extends Milk {
public GuangMinMilk() {
this.name = "光明牛奶";
}
}
GuangMinYogurt
package example;
public class GuangMinYogurt extends Milk {
public GuangMinYogurt() {
this.name = "光明酸奶";
}
}
MilkFactory(*)
在工厂中,我们需要加载配置文件,获取全类名并创建该类对象进行存储,采用static静态代码块进行加载配置文件并创建实例
- 使用HashMap进行存储
- 使用
new Properties()
创建Properties对象 - Properties的
load()
方法进行加载 - 利用反射获取当前工厂输入流
InputStream resourceAsStream = MilkFactory.class.getClassLoader().getResourceAsStream("bean.properties");
- 遍历获取配置文件中的全类名
package example;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Properties;
import java.util.Set;
/**
* 简单静态工厂
*/
public class MilkFactory {
//加载配置文件,获取全类名并创建该类对象进行存储
//容器,存储对象
private static HashMap<String, Milk> data = new HashMap<>();
//加载配置文件(一次即可)
static {
//创建properties对象
Properties properties = new Properties();
//获取对象输入流
InputStream resourceAsStream = MilkFactory.class.getClassLoader().getResourceAsStream("bean.properties");
try {
properties.load(resourceAsStream);
//从properties中获取全类名并创建对象
Set<Object> keys = properties.keySet();
for (Object key : keys) {
String className = properties.getProperty((String) key);
//使用反射创建对象
Class<?> aClass = Class.forName(className);
Milk milk = (Milk) aClass.getDeclaredConstructor().newInstance();
data.put((String) key, milk);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static Milk createMilk(String name) {
return data.get(name);
}
}
3.测试
通过传入和bean.properties
中的键获取工厂中的对应实例
package example;
public class App
{
public static void main( String[] args )
{
Milk guangMinMilk = MilkFactory.createMilk("guangMinMilk");
System.out.println(guangMinMilk.name);
Milk yogurt = MilkFactory.createMilk("guangMinYogurt");
System.out.println(yogurt.name);
}
}