设计模式之工厂方法模式

工厂方法模式

概述

工厂方法模式,父类决定实例的生成方式,但不决定所要生成的具体的类,具体的处理全部交给子类负责,这样就可以将生成实例的框架(framework)和实际负责生成实例的类解耦。

示例程序

示例程序的作用是制作身份证,他其中有4个类

  • Product类和Factory类属于framework包,这两个类组成了生成实例的框架
  • IDCard类和IDCardFactory类负责实际的加工处理,他么数据idcard包
名字说明
frameworkProduct只定义抽象方法use的抽象类
frameworkFactory实现了create方法的抽象类
idcardIDCard实现了use方法的类
idcardIDCardFacoty实现了createProduct、registerProduct的方法的类

在这里插入图片描述

Product类

public abstract class Product {
    public abstract void use();
}

Factory类

Factory类中,声明了用于生成产品的createProduct抽象方法和注册产品的registerProduct抽象方法。生成产品和注册产品的具体处理交给子类来实现。

在这个框架中,我们定义了工厂是用来调用create方法来生成Product实例的,而create方法的实现是先调用createProduct生成产品,在调用registerProduct来注册产品

public abstract class Factory {
    public final Product create(String owner){
        Product product = createProduct(owner);
        registerProduct(product);
        return product;
    }
    
    public abstract Product createProduct(String owner);
    
    public abstract void registerProduct(Product product);
}

IDCard类

@Slf4j
@Data
public class IDCard extends Product {
    private String owner;

    IDCard(String owner) {
        log.info("制作{}的身份证",owner);
        this.owner = owner;
    }

    @Override
    public void use() {
        log.info("使用{}的身份证",owner);
    }
}

IDCardFactory类

IDCardFactory类实现了createProduct方法和registerProduct方法

createProduct方法通过生成IDCard的实例来生产产品

registerProduct方法通过将IDCard的持有人(owner)保存到owners字段中来实现注册产品

public class IDCardFactory extends Factory {
    private List<String> owners = new ArrayList<>();
    
    @Override
    public Product createProduct(String owner) {
        return new IDCard(owner);
    }

    @Override
    public void registerProduct(Product product) {
        IDCard idCard = (IDCard) product;
        owners.add(idCard.getOwner());
    }
    
    public List getOwners(){
        return owners;
    }
}

测试

@SpringBootTest
class Practice403ApplicationTests {
    @Test
    void contextLoads() {
        Factory factory = new IDCardFactory();
        Product card01 = factory.create("张三");
        Product card02 = factory.create("李四");
        Product card03 = factory.create("王五");
        card01.use();
        card02.use();
        card03.use();
    }
}

//结果如下
//制作张三的身份证
//制作李四的身份证
//制作王五的身份证
//使用张三的身份证
//使用李四的身份证
//使用王五的身份证

工厂方法模式登场角色

  • Product(产品)

    Product角色属于框架的一方,是一个抽象类,他定义了工厂方法模式中生成的那些实例所持有的接口,但具体的处理由子类ConcreteProduct来实现,在示例程序中由Product类扮演这一角色

  • Creator(创建者)

    Creator角色属于框架的一方,他是负责生成Product角色的抽象类,但具体的处理由子类ConcreteCreator角色决定,在示例程序中由Factory来扮演这一角色

    Creator角色对于ConcreteCreator角色一无所知,他唯一知道的是只要调用Product角色和生成实例的方法(factoryMethod)就可以生成Product实例。在示例程序中,CreateProduct方法是生成实例的方法。不用new关键字来生成实例,而是调用生成实例的专用方法来生成实例,这一就可以防止父类与其他具体类耦合。

  • ConcreteProduct(具体的产品)

    ConcreteProduct角色属于具体实现的一方,他决定了具体的产品,在示例程序中由IDCard来扮演这一角色

  • ConcreteCreator(具体的创建者)

    ConcreteCreator角色属于具体实现的一方,他负责生成具体的产品,在示例程序中由IDCardFactory来扮演着一角色

在这里插入图片描述

练习题

习题1

**问:**在示例程序中,IDCard类的构造函数并不是public,请问这是想表达什么意思?

**答:**这是为了保证除IDCard以外的类,不能通过new的方法来创建IDCard实例,强制他们通过IDCardFactory来创建IDCard实例

@Slf4j
@Data
public class IDCard extends Product {
    private String owner;

    IDCard(String owner) {
        log.info("制作{}的身份证",owner);
        this.owner = owner;
    }

    @Override
    public void use() {
        log.info("使用{}的身份证",owner);
    }
}

习题2

修改实例程序,为IDCard类添加卡的编号,并在IDCardFactory类中保存编号与所有者之间的对应表

要实现上述需求,在我们的实例程序中不必修改框架中的内容,只需要修改IDCard以及IDCardFactory即可

@Slf4j
@Data
public class IDCard extends Product {
    private String owner;

    private Integer cardNumber;

    IDCard(String owner,Integer cardNumber) {
        log.info("制作{}的身份证,卡号为{}",owner,cardNumber);
        this.owner = owner;
        this.cardNumber = cardNumber;
    }

    @Override
    public void use() {
        log.info("使用{}的身份证",owner);
    }
}

public class IDCardFactory extends Factory {
    private HashMap<Integer,String> dataBase = new HashMap<>();

    int cardNumber = 0;

    @Override
    public Product createProduct(String owner) {
        return new IDCard(owner,cardNumber++);
    }

    @Override
    public void registerProduct(Product product) {
        IDCard idCard = (IDCard) product;
        dataBase.put(idCard.getCardNumber(),idCard.getOwner());
    }

    public HashMap<Integer,String> getOwners(){
        return dataBase;
    }
}

//测试
@Test
void test01(){
  IDCardFactory factory = new IDCardFactory();
  Product card01 = factory.create("张三");
  Product card02 = factory.create("李四");
  Product card03 = factory.create("王五");
  card01.use();
  card02.use();
  card03.use();
  HashMap<Integer,String> owners = factory.getOwners();
  for (Map.Entry<Integer,String> entry : owners.entrySet()){
    log.info("{}:{}",entry.getKey(),entry.getValue());
  }
}

//结果
//0:张三
//1:李四
//2:王五

习题3

**问:**为了强制调用方向Product类的子类的构造函数中传入“产品名称”作为参数,我们采用如下的定义方式,但是在编译代码时却出现了编译错误,这是为什么?

**答:**在Java中无法定义抽象类的构造函数,构造函数是不会被挤成的,因此定义抽象的构造函数没有任何意义。要想实现习题中的要求,应当另外声明一个设置产品名称的专用方法

//错误示例
public abstract class Product {
    public abstract Product(String name);
    public abstract void use();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值