工厂方法模式(简单工厂+工厂方法+抽象工厂)


前言

  • 内容搜集于网上多篇优秀文章,结合自己理解,写下该文

介绍

  • 工厂模式有三种类型:简单工厂模式、工厂模式 、抽象工厂模式
  • 都属于类的创建型模式
  • 创建对象的逻辑在工厂中,不会对外暴露创建逻辑

应用场景

  • 凡是需要生成复杂实例的地方,都可以尝试考虑使用工厂模式来代替

我的应用场景

  • 文件操作
    • 文件操作接口定义所有文件上传的共同行为:上传、下载、删除…
    • 三个文件操作的实现:monio、fastdfs、本地
    • 工厂类返回不同的文件操作的实例

一、简单工厂模式

介绍

  • 简单工厂模式专门定义一个工厂类来负责创建其他类的实例,可以根据参数的不同返回不同类的实例,返回值一般是一个接口、抽象类,所以被创建的实例通常都具有共同的父类、实现共同的接口
  • 在简单工厂模式中创建实例的方法通常为静态方法,所以又叫静态工厂方法模式

优点

  • 低耦合:如果类的构造过于复杂,直接在其他业务类内使用,当该对象的构造过程发生改变,就需要一个一个查找该对象并修改,对象的创建和业务类耦合严重;用工厂类生产对象来代替对象的构造过程,在业务逻辑处不会暴漏具体类,所以无需修改获取产品、使用产品的代码,只需改变工厂类的生产逻辑即可

缺点

  • 违背了 “开闭原则”,新增一个产品就需要修改工厂获取实例的方法

角色

  • 抽象产品:接口或抽象类,定义了产品共同的行为
  • 具体产品:抽象产品的实现,实现或继承抽象产品,实现具体的行为
  • 具体工厂:提供创建产品的方法,工厂类可以被外界直接调用,创建所需产品

代码实现

一、普通

1.创建产品接口
/**
 * 产品接口-网球
 *
 * @author kimi
 * @date 2022/10/12
 */
public interface ITennis{

  /** 打网球 */
  void play();

}
2.创建产品的实现
/**
 * 产品实现-百宝力网球
 *
 * @author kimi
 * @date 2022/10/12
 */
public class BabolatTennisImpl implements ITennis{

  @Override
  public void play(){
    System.err.println("play babolat tennis");
  }

}

/**
 * 产品实现-海德网球
 *
 * @author kimi
 * @date 2022/10/12
 */
public class HeadTennisImpl implements ITennis{

  @Override
  public void play(){
    System.err.println("play head tennis");
  }

}
3.创建生产产品实例的工厂
/**
 * 网球工厂
 *
 * @author kimi
 * @date 2022/10/12
 */
public class TennisFactory{

  /** 网球品牌的枚举,用于品牌判断 */
  public enum TennisBrand{

    BABOLAT,

    HEAD;
  }

  /**
   * 根据品牌的不同返回不同网球的实例
   * @param tennisBrand
   * @return
   */
  public static ITennis getTennis(TennisBrand tennisBrand){
    switch(tennisBrand){
      case BABOLAT:
        return new BabolatTennisImpl();
      case HEAD:
        return new HeadTennisImpl();
    }
    return null;
  }
}
4.生成实例
//生产百宝力网球
ITennis tennis=TennisFactory.getTennis(TennisFactory.TennisBrand.BABOLAT);

二、Spring

1.抽象产品
/**
 * 产品接口-网球
 *
 * @author kimi
 * @date 2022/10/12
 */
public interface ITennis{

  /** 打网球 */
  void play();

}
2.具体产品
/**
 * 产品实现-百宝力网球
 *
 * @author kimi
 * @date 2022/10/12
 */
//Bean name使用品牌枚举的name值
@Service("BABOLAT")
public class BabolatTennisImpl implements ITennis{

  //需要注入的属性

  @Override
  public void play(){
    System.err.println("play babolat tennis");
  }

}

/**
 * 产品实现-海德网球
 *
 * @author kimi
 * @date 2022/10/12
 */
//Bean name使用品牌枚举的name值
@Service("HEAD")
public class HeadTennisImpl implements ITennis{

  //需要注入的属性

  @Override
  public void play(){
    System.err.println("play head tennis");
  }

}
3.具体工厂
/**
 * 网球工厂
 *
 * @author kimi
 * @date 2022/10/12
 */
@Component
public class TennisFactory{

  //注入ITennis的所有实现类
  @Autowired
  private Map<String,ITennis> tennisMap;

  /** 网球品牌的枚举,用于品牌判断 */
  public enum TennisBrand{

    BABOLAT,

    HEAD;
  }

  /**
   * 根据品牌的不同返回不同网球的实例
   * @param tennisBrand
   * @return
   */
  public ITennis getTennisInstance(TennisBrand tennisBrand){
    return tennisMap.get(tennisBrand.name());
  }
}
4.生成实例
@Resource
private TennisFactory tennisFactory;

tennisFactory.getTennisInstance(TennisFactory.TennisBrand.BABOLAT);

二、工厂方法模式

优点

  • 简单工厂模式有一个问题就是,类的创建依赖工厂类,如果增加了一种产品实现,就必须对工厂类返回产品实例的方法进行修改,工厂方法解决了这个问题
  • 遵循"开闭原则",增加抽象工厂、每个产品增加对应工厂类,每个工厂类实现生产对应产品,这样再增加产品时,只需增加一个包,在其中增加产品实现、产品工厂实现即可,而无需修改任何代码

角色

  • 抽象产品:接口或抽象类,定义了产品共同的行为
  • 抽象工厂:提供创建产品的抽象方法
  • 具体产品:抽象产品的实现,实现或继承抽象产品,实现具体的行为
  • 具体工厂:抽象工厂的实现,每个具体产品都对应一个具体工厂,生产对应的具体产品实例

代码实现

1.抽象产品

/**
 * 抽象产品-网球
 * 抽象产品和抽象工厂在一个包
 *
 * @author kimi
 * @Date 2022/7/4
 */
public interface ITennis{

  /**
   * 打网球
   * 每个产品的实现法不同,需要产品的实现实现具体的打网球
   */
  void play();
}

2.抽象工厂

/**
 * 抽象工厂-网球工厂
 * 抽象产品和抽象工厂在一个包
 *
 * @author kimi
 * @Date 2022/7/4
 */
public interface ITennisFactory{

  /**
   * 定义生产产品实例接口
   * 每个创建产品实例的工厂方法都不同,实现类来实现具体的方法
   */
  ITennis getTennisInstance();

}

3.具体产品

/**
 * 具体产品-百宝力网球
 * 产品实现和生产对应产品的工厂类实现在一个包
 *
 * @author kimi
 * @Date 2022/7/4
 */
public class BabolatTennis implements ITennis{

  /**
   * 访问控制修饰符限制了该产品只能在 本类、同包类 中创建,这里是指让BabolatTennisFactory来创建
   */
  BabolatTennis(){

  }

  @Override
  public void play(){
    System.err.println("play babolat tennis");
  }
}

4.具体工厂

/**
 * 具体工厂-百宝力工厂类
 * 产品实现和生产对应产品的工厂类实现在一个包
 *
 * @author kimi
 * @Date 2022/7/4
 */
public class BabolatTennisFactory implements ITennisFactory{


  /**
   * 生产百宝力网球
   * @return
   */
  @Override
  public ITennis getTennisInstance(){
    return new BabolatTennis();
  }
}

5.生成实例

//打百宝力网球
TennisFactory babolatFactory=new BabolatTennisFactory();
ITennis babolatTennis=babolatFactory.getTennisInstance();
babolatTennis.play();

三、抽象工厂方法模式

介绍

  • 抽象工厂模式是工厂方法模式的升级版本。与工厂方法模式的区别在于:工厂方法模式针对的是一个产品结构;而抽象工厂模式则是针对的一组产品结构
  • 工厂方法有一个问题:增加一种产品,就要增加抽象产品、抽象工厂、具体产品、具体工厂。有的时候产品之间是有关系的,更像是一组产品,例如网球、网球拍。不区分厂商而是归为一类产品的叫做产品族。海德生产网球、网球拍,它们属于同一个厂商

角色

  • 抽象产品:接口或抽象类,定义了产品共同的行为,这里是多个抽象产品类
  • 抽象工厂:提供创建产品的抽象方法,这里是多个创建产品的抽象方法
  • 具体产品:抽象产品的实现,实现或继承抽象产品,实现具体的行为,这里会多个具体产品类
  • 具体工厂:抽象工厂的实现,生产对应的产品的实例,这里会多个生产产品的方法

代码实现

1.抽象产品

/**
 * 抽象产品-网球
 * 抽象产品和抽象工厂在一个包
 *
 * @author kimi
 * @date 2022/10/16
 */
public interface ITennis{

  /**
   * 打网球
   * 每个产品的实现法不同,需要产品的实现实现具体的打网球
   */
  void play();

}

/**
 * 抽象产品-网球拍
 * 抽象产品和抽象工厂在一个包
 *
 * @author kimi
 * @date 2022/10/16
 */
public interface ITennisRacket{

  /**
   * 使用网球拍
   * 每个产品的实现法不同,需要产品的实现实现具体的网球拍使用
   */
  void use();

}

2.抽象工厂

/**
 * 抽象工厂-生产一组产品实例
 * 抽象产品和抽象工厂在一个包
 *
 * @author kimi
 * @Date 2022/7/4
 */
public interface ITennisFactory{

  /**
   * 生产网球实例
   * @return
   */
  ITennis getTennisInstance();

  /**
   * 生产网球拍实例
   * @return
   */
  ITennisRacket getTennisRacketInstance();

}

3.具体产品

/**
 * 具体产品-网球-百宝力网球
 * 产品实现和生产对应产品的工厂类实现在一个包
 *
 * @author kimi
 * @Date 2022/7/4
 */
public class BabolatTennis implements ITennis{

  /**
   * 访问控制修饰符限制了该产品只能在 本类、同包类 中创建,这里是指让BabolatTennisFactory来创建
   */
  BabolatTennis(){

  }

  @Override
  public void play(){
    System.err.println("play babolat tennis");
  }
}

/**
 * 具体产品-网球拍-百宝力网球拍
 * 产品实现和生产对应产品的工厂类实现在一个包
 *
 * @author kimi
 * @Date 2022/7/4
 */
public class BabolatRacketTennis implements ITennisRacket{

  /**
   * 访问控制修饰符限制了该产品只能在 本类、同包类 中创建,这里是指让BabolatTennisFactory来创建
   */
  BabolatRacketTennis(){

  }

  @Override
  public void use(){
    System.err.println("use babolat tennis racket");
  }
}

4.具体工厂

/**
 * 具体工厂-百宝力工厂类
 * 产品实现和生产对应产品的工厂类实现在一个包
 *
 * @author kimi
 * @Date 2022/7/4
 */
public class BabolatTennisFactory implements ITennisFactory{

  /**
   * 生产网球实例-百宝力网球
   * @return
   */
  @Override
  public ITennis getTennisInstance(){
    return new BabolatTennis();
  }

  /**
   * 生产网球拍实例-百宝力网球拍
   * @return
   */
  @Override
  public ITennisRacket getTennisRacketInstance(){
    return new BabolatRacketTennis();
  }
}

5.生成实例

//百宝力
ITennisFactory babolatFactory=new BabolatTennisFactory();
//使用网球拍
ITennisRacket babolatTennisRacket=babolatFactory.getTennisRacketInstance();
babolatTennisRacket.use();
//打网球
ITennis babolatTennis=babolatFactory.getTennisInstance();
babolatTennis.play();
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kimi-001

只想在有限的时间分享更多的知识

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值