工厂模式
工厂模式
什么是工厂模式?
工厂模式是一种创建对象的设计模式,用来替代传统的 如 A a=new A() 创建对象的方式,提供了一种 统一的接口来创建对象,
封装创建的过程,达到解耦的目的。
优缺点
- 优点
- 封装对象的创建过程:工厂类将对象创建的细节封装起来,客户端无需关心实例化的具体过程,简化了代码。
- 提高代码的可维护性:由于实例化逻辑集中在工厂类中,如果需要更改对象的创建逻辑,只需修改工厂类,不必修改客户端代码。
- 解耦,通过工厂接口来创建对象,而不用关注对象的具体实现(不用关心 a的具体实现A a = new A 还是 A a = new AFather())使得代码更易于扩展和维护。
- 支持多态和接口编程:可以返回父类或接口的对象,使得代码更加灵活,符合“依赖倒置原则”。
- 缺点
- 增加了系统的复杂性:引入工厂类会增加类的数量和代码的复杂度,对于简单的应用场景可能显得多余。
- 可能导致过度设计:在不需要复杂对象创建逻辑的情况下,使用工厂模式可能导致代码显得臃肿和过度设计
- 可能难以管理:当工厂类需要处理很多具体类的创建时,工厂类本身可能变得复杂和难以管理。
工厂模式的分类
- 简单工厂模式(Simple Factory)
- 工厂方法模式(Factory Method)
- 抽象工厂模式(Abstract Factory)
简单工厂模式(Simple Factory)
假设我们有两种动物类:Dog 和 Cat,和一个动物的接口类Animal,他们都有“说”的方法
public interface Animal {
//动物接口,公共的 说的方法
void speak();
}
public class Dog implements Animal{
@Override
public void speak() {
System.out.println("我是旺财,汪!汪!汪!");
}
}
public class Cat implements Animal{
@Override
public void speak() {
System.out.println("我是喵星人,喵!喵!喵!");
}
}
传统方式的使用
/**
* 传统模式
* 优点:简单直接
* 缺点:
* 高耦合:如果新来了牛马,那么就得在所有业务处去写
* 形如 Animal hors = new Horse() 的具体对象创建
* 不利于扩展
*/
@Test
public void useTraditionTest(){
//传统调用测试
//创建一只狗对象,并调用讲话的方法
Animal dog = new Dog();
dog.speak();//输出我是旺财,汪!汪!汪!
//创建一只猫对象,并调用讲话的方法
Animal cat = new Cat();
cat.speak();//我是喵星人,喵!喵!喵
// ... 以及未来新到来的牛马们
}
简单工厂方式的使用
- 创建简单工厂类,用来创建动物实例
public class AnimalFactory {
public static Animal getAnimal(String type){
//把创建对象的细节封装在工厂类的方法里,统一向外暴露
Animal animal = null;
//根据传入的type参数,创造不同的动物实例
if ("dog".equalsIgnoreCase(type)) {
animal = new Dog();
} else if ("cat".equalsIgnoreCase(type)) {
animal = new Cat();
}
return animal;
}
}
- 简单工厂的使用
/**
* 简单工厂模式
* 优点:
* 低耦合:厂类来封装对象的创建过程,业务处代码不依赖具体类的实现,只需知道工厂类和接口。
* 灵活性:可以在工厂类中轻松添加新类型的对象,客户端代码繁琐重复创建。
* 缺点:
* 工厂类过于复杂:随着对象种类的增加,工厂类会变得复杂和臃肿。
* 不符合开闭原则:每次新增一种牛马类都需要修改工厂类代码。
*/
@Test
public void useSimpleFactoryTest(){
//通过简单工厂获取狗对象,并调用讲话的方法
Animal dog = AnimalFactory.getAnimal("dog");
dog.speak();输出我是旺财,汪!汪!汪!
//通过简单工厂获取猫对象,并调用讲话的方法
Animal cat = AnimalFactory.getAnimal("cat");
cat.speak();我是喵星人,喵!喵!喵
}
由于简单工厂模式的缺点,每新增一种牛马,就需要修改工厂类代码,当牛马类型太多时,
造成工厂逻辑过于复杂,不利于系统的扩展维护,并且工厂类集中了所有牛马创建逻辑,一旦有牛马出现意外,
整个牛马厂都要歇菜,创建都要受到影响(这不就是中央到分布式的思想么),基于此引入了工厂方法模式。
工厂方法模式(Factory Method)
在工厂方法模式中,我们定义一个工厂接口,为每一种动物创建一个工厂去实现工厂接口,因为在现实生活中,
会存在很牛马,比如牛啊,马啊,猪啊,驴啊等等我们可以把他们的实例创建统一到狗子工厂中,便于维护和管理。
- 创建动物工厂
public interface AnimalFactoryInter {
//创建牛马的工厂接口
Animal createAnimal();
}
- 创建旺财工厂,实现动物工厂
public class DogFactory implements AnimalFactoryInter {
@Override
public Animal createAnimal() {
//旺财工厂,专门创造狗子
return new Dog();
}
}
- 创建猫星人工厂,实现动物工厂
public class CatFactory implements AnimalFactoryInter {
@Override
public Animal createAnimal() {
//猫星人工厂,专门创造喵喵
return new Cat();
}
}
- 工厂方法的使用
/**
* 工厂方法模式
* 优点:
* 遵循开闭原则:添加新类型的对象时,只需创建新的具体工厂类,无需修改现有代码。
* 符合单一职责原则:每个具体工厂类只负责创建一种特定类型的对象。
* 灵活性高:客户端代码依赖于抽象的工厂接口,可以在运行时选择具体的工厂实现。
* 缺点:
* 增加类的数量:每增加一种新类型的对象,都需要增加一个具体工厂类,可能会导致类的数量增加。
* 代码复杂度增加:相比于简单工厂模式,工厂方法模式的代码结构更加复杂。
*/
@Test
public void useFactoryMethodTest(){
//工厂方法调用方式
//通过旺财工厂,创造狗子
AnimalFactoryInter dogFactory = new DogFactory();
Animal dog = dogFactory.createAnimal();
dog.speak();//输出我是旺财,汪!汪!汪!
//通过喵星人工厂,创造喵喵喵
AnimalFactoryInter catFactory = new CatFactory();
Animal cat = catFactory.createAnimal();
cat.speak();//我是喵星人,喵!喵!喵
}
由于工厂方法模式的缺点,抽象工厂只能创建一种产品,即AnimalFactoryInter抽象工厂只能去创建动物,所以引入了抽象工厂模式,
他其实是工厂方法的一种扩展,就是在这个抽象工厂中提供多个抽象方法,除了提供创造动物的方法,还提供创造食物的方法,
再由具体的食物工厂去实现,这样当我们就可以给旺财喂点大棒骨,给喵星人喂条小咸鱼的功能了,基于此引入抽象工厂模式
抽象工厂模式(Abstract Factory)
在抽象工厂模式中,有两个抽象层次:抽象工厂和具体工厂。抽象工厂定义了一个或多个工厂方法,用于创建一组相关或依赖的产品对象。
具体工厂实现了抽象工厂中定义的工厂方法,负责创建具体的产品对象。与工厂方法模式相比,抽象工厂模式更加灵活,可以创建一组相关的产品对象,
并且可以在不修改客户端代码的情况下改变所创建的产品组合。同时,抽象工厂模式也更加复杂,因为它需要定义多个工厂方法和相关的产品接口。
在抽象工厂模式中,涉及一个需要了解的知识点
- 产品等级结构:产品等级结构指的是产品的继承结构,比如生活方式等级,可以有1号人生的等级,2号人生等级,在每个等级结构中,包含了产品族中具体的产品类
如:生产一只金毛狗(Dog),生产一只大棒骨(DogFood),生成一套总统套房的狗窝(DogHouse),实现产品族中具体的工厂生成类(DogFactory)
动物这一产品等级 - 产品族:一组相关的产品对象,它们通常是一起使用的,并且由同一个工厂类创建。例如,动物工厂中的产品族可以包括狗和猫。如定义Animal动物产品,在动物产品族中
会生成出金毛狗,折耳猫,拉磨驴等不同种类的产品
结构图:
如果动物这个例子不太容易理解,可以再增加一个产品的例子,把冰箱、空调、电视对应为产品,把美的品牌、格力品牌、海尔品牌对应为生产的工厂
那么 美的工厂生产的冰箱、空调、电视 就是一个产品族(产品家族,这个品牌有很多种类的产品),
而美的冰箱、格力冰箱、海尔冰箱形成一个产品等级结构(同一种产品有1级,2级,3级区别,也知道谁是冰箱等级里的老大)
结构图:
- 新增食物接口
public interface Food {
//食物接口,公共的 吃的方法
void eat();
}
- 旺财相关具体的食物类,
public class DogFood implements Food{
@Override
public void eat() {
System.out.println("我是给旺财专供的大棒骨!");
}
}
- 喵星人相关的食物类
public class CatFood implements Food{
@Override
public void eat() {
System.out.println("我是给喵星人专供的小咸鱼!");
}
}
- 在动物工厂(也可称为超级工厂)中新增 生产食物的方法
public interface AnimalFactoryInter {
//创建牛马的工厂接口
Animal createAnimal();
//定义牛马们吃饭的接口
Food createFood();
}
- 在狗工厂中来实现 食物的生产
public class DogFactory implements AnimalFactoryInter {
@Override
public Animal createAnimal() {
//旺财工厂,专门创造狗子
return new Dog();
}
@Override
public Food createFood() {
//旺财工厂,专门创造狗粮
return new DogFood();
}
}
- 在猫工厂中来实现 食物的生产
public class CatFactory implements AnimalFactoryInter {
@Override
public Animal createAnimal() {
//猫星人工厂,专门创造喵喵
return new Cat();
}
@Override
public Food createFood() {
//猫星人工厂,专门创造猫粮
return new CatFood();
}
}
- 抽象工厂的使用
/**
* 抽象工厂模式
* 优点:
* 符合开闭原则:添加新产品族(增加鸟类)时,只需增加新的具体工厂类和相应的产品类,不需要修改现有代码。
* 高内聚低耦合:具体工厂类负责创建相关产品的对象,从而使客户端代码与具体工厂类的实现细节解耦。
* 增强产品族的一致性:确保一个产品族中的产品对象在创建时的一致性。
* 缺点:
* 复杂性增加:与简单工厂和工厂方法模式相比,抽象工厂模式需要更多的类和接口,代码结构更复杂。
* 扩展产品等级结构困难:添加新的产品类型(例如,在已有的动物和食物的基础上,再添加一个新的房子类),
* 则需要修改抽象工厂接口,添加新的方法以创建新的产品类型,同时修改所有的具体工厂类以实现新的方法
*/
@Test
public void useAbstractFactoryTest(){
//抽象工厂 调用方式
//通过工厂,创造狗子,并生产狗粮
AnimalFactoryInter dogFactory = new DogFactory();
Animal dog = dogFactory.createAnimal();
Food dogFood = dogFactory.createFood();;
dog.speak();//我是旺财,汪!汪!汪!
dogFood.eat();//我是给旺财专供的大棒骨!
//通过工厂,创造喵星人,并生产猫粮
AnimalFactoryInter catFactory = new CatFactory();
Animal cat = catFactory.createAnimal();
Food catFood = catFactory.createFood();;
cat.speak();//我是喵星人,喵!喵!喵!
catFood.eat();//我是给喵星人专供的小咸鱼!
}
完整调用测试示例
public class UseTest {
/**
* 传统模式
* 优点:简单直接
* 缺点:
* 高耦合:如果新来了牛马,那么就得在所有业务处去写 形如 Animal hors = new Horse() 的具体对象创建
* 不利于扩展
*/
@Test
public void useTraditionTest(){
//传统调用测试
//创建一只狗对象,并调用讲话的方法
Animal dog = new Dog();
dog.speak();//输出我是旺财,汪!汪!汪!
//创建一只猫对象,并调用讲话的方法
Animal cat = new Cat();
cat.speak();//我是喵星人,喵!喵!喵
// ... 以及未来新到来的牛马们
}
/**
* 简单工厂模式
* 优点:
* 低耦合:厂类来封装对象的创建过程,业务处代码不依赖具体类的实现,只需知道工厂类和接口。
* 灵活性:可以在工厂类中轻松添加新类型的对象,客户端代码繁琐重复创建。
* 缺点:
* 工厂类过于复杂:随着对象种类的增加,工厂类会变得复杂和臃肿。
* 不符合开闭原则:每次新增产品类都需要修改工厂类代码。
*/
@Test
public void useSimpleFactoryTest(){
//简单工厂调用方式
//通过简单工厂获取狗对象,并调用讲话的方法
Animal dog = AnimalFactory.getAnimal("dog");
dog.speak();//输出我是旺财,汪!汪!汪!
//通过简单工厂获取猫对象,并调用讲话的方法
Animal cat = AnimalFactory.getAnimal("cat");
cat.speak();//我是喵星人,喵!喵!喵
}
/**
* 工厂方法模式
* 优点:
* 遵循开闭原则:添加新类型的对象时,只需创建新的具体工厂类,无需修改现有代码。
* 符合单一职责原则:每个具体工厂类只负责创建一种特定类型的对象。
* 灵活性高:客户端代码依赖于抽象的工厂接口,可以在运行时选择具体的工厂实现。
* 多态性的应用:使得客户端代码可以统一通过抽象工厂类来调用工厂方法,而具体的工厂子类根据需求返回不同的产品对象,实现了对象的动态切换和灵活性。
* 缺点:
* 增加类的数量:每增加一种新类型的对象,都需要增加一个具体工厂类,可能会导致类的数量增加。
* 代码复杂度增加:相比于简单工厂模式,工厂方法模式的代码结构更加复杂。
*/
@Test
public void useFactoryMethodTest(){
//工厂方法调用方式
//通过旺财工厂,创造狗子
AnimalFactoryInter dogFactory = new DogFactory();
Animal dog = dogFactory.createAnimal();
dog.speak();//输出我是旺财,汪!汪!汪!
//通过喵星人工厂,创造喵喵喵
AnimalFactoryInter catFactory = new CatFactory();
Animal cat = catFactory.createAnimal();
cat.speak();//我是喵星人,喵!喵!喵
}
/**
* 抽象工厂模式
* 优点:
* 符合开闭原则:添加新产品族(增加鸟类)时,只需增加新的具体工厂类和相应的产品类,不需要修改现有代码。
* 高内聚低耦合:具体工厂类负责创建相关产品的对象,从而使客户端代码与具体工厂类的实现细节解耦。
* 增强产品族的一致性:确保一个产品族中的产品对象在创建时的一致性。
* 缺点:
* 复杂性增加:与简单工厂和工厂方法模式相比,抽象工厂模式需要更多的类和接口,代码结构更复杂。
* 扩展产品等级结构困难:添加新的产品类型(例如,在已有的动物和食物的基础上,再添加一个新的房子类),
* 则需要修改抽象工厂接口,添加新的方法以创建新的产品类型,同时修改所有的具体工厂类以实现新的方法
*/
@Test
public void useAbstractFactoryTest(){
//抽象工厂 调用方式
//通过工厂,创造狗子,并生产狗粮
AnimalFactoryInter dogFactory = new DogFactory();
Animal dog = dogFactory.createAnimal();
Food dogFood = dogFactory.createFood();;
dog.speak();//我是旺财,汪!汪!汪!
dogFood.eat();//我是给旺财专供的大棒骨!
//通过工厂,创造喵星人,并生产猫粮
AnimalFactoryInter catFactory = new CatFactory();
Animal cat = catFactory.createAnimal();
Food catFood = catFactory.createFood();;
cat.speak();//我是喵星人,喵!喵!喵!
catFood.eat();//我是给喵星人专供的小咸鱼!
}
}