工厂模式的作用
解耦 :把对象的创建和使用的过程分开
降低代码重复: 如果创建某个对象的过程都很复杂,需要一定的代码量,而且很多地方都要用到,那么就会有很多的重复代码。
降低维护成本 :由于创建过程都由工厂统一管理,所以发生业务逻辑变化,不需要找到所有需要创建对象B的地方去逐个修正,只需要在工厂里修改即可,降低维护成本。
工厂模式的分类
- 简单工厂模式(Simple Factory)
又称静态工厂方法模式(Static Factory Method Pattern) - 工厂方法模式(Factory Method)
又称多态性工厂(Polymorphic Factory)模式或虚拟构造子(Virtual Constructor)模式 - 抽象工厂模式(Abstract Factory)
又称工具箱(Kit 或Toolkit)模式
简单工厂模式
简单工厂模式其实并不算是一种设计模式,更多的时候是一种编程习惯。
例:
创建一个宠物接口
public interface Pet {
void say();
}
创建实现该接口的宠物实体类(猫、狗、鸟)
public class Cat implements Pet {
public void say() {
System.out.println("我是一只猫,喵喵喵");
}
}
public class Dog implements Pet {
public void say() {
System.out.println("我是一只狗,汪汪汪");
}
}
public class Bird implements Pet {
public void say() {
System.out.println("我是一只鸟,叽叽叽喳喳喳");
}
}
创建要给宠物实体工厂类,根据给定的信息生成宠物实体类对象
public class PetsFactory {
public static Pet createPet(String type){
Pet pet = null;
if ("Cat".equals(type)){
return new Cat();
}else if ("Dog".equals(type)){
return new Dog();
}else if ("Bird".equals(type)){
return new Bird();
}
return pet;
}
}
测试一下
public class Test {
public static void main(String[] args) {
Pet cat = PetsFactory.createPet("Cat");
Pet dog = PetsFactory.createPet("Dog");
Pet bird = PetsFactory.createPet("Bird");
cat.say();
dog.say();
bird.say();
}
}
结果
我是一只猫,喵喵喵
我是一只狗,汪汪汪
我是一只鸟,叽叽叽喳喳喳
这样会有一个问题,如果我们新增了一个种类的宠物,比如兔子,我们就要修改工厂类的 createPet() 方法,违背了开放封闭原则
使用反射机制来改善简单工厂
新的工厂类
public class PetsFactory2 {
public static Object getClass(Class<? extends Pet> clazz){
Object obj = null;
try {
obj = Class.forName(clazz.getName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return obj;
}
}
新增兔子
public class Rabbit implements Pet{
public void say() {
System.out.println("我是一只兔子,我好像不会叫");
}
}
测试
public class Test2 {
public static void main(String[] args) {
Cat cat = (Cat) PetsFactory2.getClass(Cat.class);
cat.say();
Dog dog = (Dog) PetsFactory2.getClass(Dog.class);
dog.say();
Bird bird = (Bird) PetsFactory2.getClass(Bird.class);
bird.say();
Rabbit rabbit = (Rabbit) PetsFactory2.getClass(Rabbit.class);
rabbit.say();
}
}
结果
我是一只猫,喵喵喵
我是一只狗,汪汪汪
我是一只鸟,叽叽叽喳喳喳
我是一只兔子,我好像不会叫
工厂方法模式
在工厂方法模式中,每个对象都有一个与之对应的工厂
例:
简单工厂例子中的宠物接口和相关实现类保留不变
增加工厂接口
public interface PetFactory {
public Pet getPet();
}
增加工厂类,实现工厂接口
public class CatFactory implements PetFactory{
public Pet getPet() {
return new Cat(); //返回猫
}
}
public class DogFactory implements PetFactory{
public Pet getPet() {
return new Dog(); //返回狗
}
}
public class BirdFactory implements PetFactory{
public Pet getPet() {
return new Bird(); //返回鸟
}
}
测试一下
public class Test {
public static void main(String[] args) {
PetFactory catFactory = new CatFactory();
catFactory.getPet().say();
PetFactory dogFactory = new DogFactory();
dogFactory.getPet().say();
PetFactory birdFactory = new BirdFactory();
birdFactory.getPet().say();
}
}
结果
我是一只猫,喵喵喵
我是一只狗,汪汪汪
我是一只鸟,叽叽叽喳喳喳
抽象工厂模式
在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。拿宠物店来比较,工厂方法模式的宠物店只卖宠物,想要买饲料还要去工厂方法模式的饲料店。抽象工厂模式的宠物店不仅卖宠物,还卖饲料,甚至还给宠物看病。这样铲屎官们就不用为了自家主子东奔西走了
例:
宠物接口和相关实现类保留不变
饲料相关接口和类
public interface Food {
public void feed();
}
public class DogFood implements Food{
public void feed() {
System.out.println("狗粮喂给狗狗");
}
}
public class CatFood implements Food{
public void feed() {
System.out.println("猫粮喂给猫咪");
}
}
public class BirdFood implements Food{
public void feed() {
System.out.println("虫子喂给小鸟");
}
}
public class RabbitFood implements Food{
public void feed() {
System.out.println("胡萝卜喂给兔子");
}
}
创建工厂接口
public interface PetFactory {
public Pet buyPet();
public Food buyFood();
}
具体工厂
public class CatFactory implements PetFactory{
public Pet buyPet() {
return new Cat();
}
public Food buyFood() {
return new CatFood();
}
}
public class DogFactory implements PetFactory{
public Pet buyPet() {
return new Dog();
}
public Food buyFood() {
return new DogFood();
}
}
public class BirdFactory implements PetFactory{
public Pet buyPet() {
return new Bird();
}
public Food buyFood() {
return new BirdFood();
}
}
测试一下
public class Test {
public static void main(String[] args) {
PetFactory factory;
Food food;
Pet pet;
factory = new CatFactory();
food = factory.buyFood();
pet = factory.buyPet();
food.feed();
pet.say();
factory = new DogFactory();
pet = factory.buyPet();
food = factory.buyFood();
food.feed();
pet.say();
factory = new BirdFactory();
pet = factory.buyPet();
food = factory.buyFood();
food.feed();
pet.say();
}
}
结果
猫粮喂给猫咪
我是一只猫,喵喵喵
狗粮喂给狗狗
我是一只狗,汪汪汪
虫子喂给小鸟
我是一只鸟,叽叽叽喳喳喳
简单工厂改造抽象工厂
public class PetFactory {
private String petName;
public PetFactory(String petName) {
this.petName = petName;
}
public Pet getPet(){
Pet pet = null;
switch (petName) {
case "bird":
pet = new Bird();
break;
case "cat":
pet = new Cat();
break;
case "dog":
pet = new Dog();
break;
}
return pet;
}
public Food getFood(){
Food food = null;
switch (petName) {
case "bird":
food = new BirdFood();
break;
case "cat":
food = new CatFood();
break;
case "dog":
food = new DogFood();
break;
}
return food;
}
}
public class Test {
public static void main(String[] args) {
PetFactory petFactory;
Pet pet;
Food food;
petFactory = new PetFactory("cat");
pet = petFactory.getPet();
food = petFactory.getFood();
food.feed();
pet.say();
petFactory = new PetFactory("bird");
pet = petFactory.getPet();
food = petFactory.getFood();
food.feed();
pet.say();
petFactory = new PetFactory("dog");
pet = petFactory.getPet();
food = petFactory.getFood();
food.feed();
pet.say();
}
}
反射改造的抽象工厂
新增产品不用修改工厂方法,但对类名有一致性要求。
public class PetFactory {
private String petName;
public PetFactory(String petName) {
this.petName = petName;
}
public Pet getPet() {
String packageName = "com.example.pet.";
String clazzName = packageName + petName;
Pet pet = null;
try {
Class clazz = Class.forName(clazzName);
pet = (Pet) clazz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return pet;
}
public Food getFood() {
String packageName = "com.example.food.";
String suffix = "Food";
String clazzName = packageName + petName + suffix;
Food food = null;
try {
Class clazz = Class.forName(clazzName);
food = (Food) clazz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return food;
}
}
public class Test {
public static void main(String[] args) {
PetFactory petFactory;
Food food;
Pet pet;
petFactory = new PetFactory("Dog");
food = petFactory.getFood();
pet = petFactory.getPet();
food.feed();
pet.say();
petFactory = new PetFactory("Cat");
food = petFactory.getFood();
pet = petFactory.getPet();
food.feed();
pet.say();
petFactory = new PetFactory("Rabbit");
food = petFactory.getFood();
pet = petFactory.getPet();
food.feed();
pet.say();
}
}