设计模式的诞生,在我个人的见解里认为其最终目的就是为了达到程序设计过程中各功能模块的解耦。
程序设计遵循高内聚低耦合的概念,这是软件工程发展过程中一个由始至终贯穿的主旨。
工厂模式作为面向对象程序设计语言中一种经典的设计模式,即使是在技术日新月异的今天,依旧有着它存在和被我们学以致用的价值。
工厂模式分为三种,它们分别是:简单工厂模式,工厂模式和抽象工厂模式。三种模式并没有孰强孰弱的分别,而是应该由开发人员根据自身经验和具体业务场景选择其中一种最适合的模式来实现需求。
下面我将以我自己的理解依次解析上述三种设计模式的实现步骤与细节。
1.简单工厂
简单工厂模式要求有一个工厂类提供对象的实例,从生产者和消费者的角度来看,这个工厂类可以看成是生产者,生产类的实例对象供消费者调用,消费者通常就是调用该实例的类。
下面是一个简单的场景,定义一个生产食物的工厂类FoodFactory,该工厂会生产三种食物,它们分别是米饭Rice,面包Bread,肌肉Chicken,由于它们都是食物,拥有食物的共性,所以都会实现一个Food接口,这个接口里只定义一个方法printName,不同的实现类中该方法实现逻辑各不相同。
首先是Food接口
package com.huang.food;
public interface Food {
public void printName();
}
然后是三个实现类Rice,Bread以及Chicken
package com.huang.food.impl;
import com.huang.food.Food;
public class Rice implements Food{
@Override
public void printName() {
System.out.println("rice");
}
}
public class Bread implements Food{
@Override
public void printName() {
System.out.println("bread");
}
}
public class Chicken implements Food{
@Override
public void printName() {
System.out.println("chicken");
}
}
接下来是工厂类FoodFactory,它会包含具体生产哪一个实体类对象的判断逻辑,静态方法produceFood的返回类型为Food接口,但是在分支语句中其实返回的是该接口的具体实现类,实际上这是java中多态的体现。
package com.huang.factory;
import com.huang.food.Food;
import com.huang.food.impl.Bread;
import com.huang.food.impl.Chicken;
import com.huang.food.impl.Rice;
public class FoodFactory {
public static Food pruduceFood(String type){
switch (type) {
case "米饭":
return new Rice();
case "鸡肉":
return new Chicken();
case "面包":
return new Bread();
default:
return null;
}
}
}
最后是测试类TestFactory
package com.huang.FactoryTest;
import com.huang.factory.FoodFactory;
public class TestFactory {
public static void main(String[] args) {
FoodFactory.pruduceFood("米饭").printName();
FoodFactory.pruduceFood("鸡肉").printName();
}
}
总结一下,实际上简单工厂的核心概念就是创造一个工厂类,让调用对象实例时由工厂类返回实例。当然这些实例在某种程度上可以被抽象,即它们都实现了共同的接口。
简单工厂实际上并不遵循软件设计中的开闭原则,即对扩展开放,对修改封闭。上述例子中,如果生产第四种食物比如Cookie,那么需要修改工厂类的代码,在写好的代码中再加入新的分支语句逻辑,违背了开闭原则。并且当要生产的产品很多时,工厂类中的分支语句会很庞大,这也是它的缺点,但是这不影响它至今存在的意义,它的诞生依旧一定程度解决了代码的耦合,革新了传统的开发过程。
2.工厂模式
工厂模式相比简单工厂模式,实际上就是将工厂类抽象成接口,生产不同实例的工厂类均实现该抽象接口。
从生产者和消费者的角度,就是将生产者进行进一步抽象。
首先是生产类的抽象接口Factory
package com.huang.factory;
import com.huang.food.Food;
public interface FoodFactory {
public Food produceFood();
}
工厂模式中的工厂实现类不再像简单工厂中的工厂类一样具有生产不同实例的判断逻辑,而是通过实现接口的方式分成生产不同实例对象的工厂,以下是生产三种实例对象的工厂实现类
package com.huang.factory.impl;
import com.huang.factory.FoodFactory;
import com.huang.food.Food;
import com.huang.food.impl.Bread;
public class BreadFactory implements FoodFactory{
@Override
public Food produceFood() {
return new Bread();
}
}
public class ChickenFactory implements FoodFactory{
@Override
public Food produceFood() {
return new Chicken();
}
}
public class RiceFactory implements FoodFactory{
@Override
public Food produceFood() {
// TODO Auto-generated method stub
return new Rice();
}
}
实例对象接口和实现类与简单工厂相同,不再赘述。
最后是实现类TestFactory,直接通过多态的方式实例化对应的工厂实现类,相较于简单工厂更加细腻。如果增加实例对象的话只需要增加对应的实现类,完全遵循开闭原则(对修改封闭,对扩展开放)。
package com.huang.testFactory;
import com.huang.factory.FoodFactory;
import com.huang.factory.impl.ChickenFactory;
import com.huang.factory.impl.RiceFactory;
public class TestFactory {
public static void main(String[] args) {
FoodFactory factory1 = new RiceFactory();
factory1.produceFood().printName();
FoodFactory factory2 = new ChickenFactory();
factory2.produceFood().printName();
}
}
3.抽象工厂
抽象工厂相较于上述两种模式,更具有抽象的特点。
当所需要的实例可以抽象出多个接口时,换句话说,当我们的要调用的一个或多个对象具备交叉的几种属性时,我们可以把这几种属性分别抽象出来,变成不同的工厂接口,对应的实现类可以更详细的生产不同的实例。
下面是一个场景,有四种食物分别为beef牛肉,chicken鸡肉,tomato西红柿,potato土豆,它们按照种类可以分为肉类:beef,chicken。蔬菜:tomato,potato。按照价格可以分为便宜:chicken,tomato。昂贵:beef,potato。我们把种类抽象成生成实例的接口,把价格昂贵抽象成工厂类要实现的接口,每个工厂都有生产肉类和蔬菜的功能。
对应到代码,首先是实例接口Meat和Vagetable以及它们的实现类:
package com.huang.food;
public interface Meat {
public void printName();
}
package com.huang.food;
public interface Vagetable {
public void printName();
}
package com.huang.food.impl;
import com.huang.food.Meat;
public class Beef implements Meat{
@Override
public void printName() {
// TODO Auto-generated method stub
System.out.println("beef");
}
}
package com.huang.food.impl;
import com.huang.food.Meat;
public class Chicken implements Meat{
@Override
public void printName() {
// TODO Auto-generated method stub
System.out.println("chicken");
}
}
package com.huang.food.impl;
import com.huang.food.Vagetable;
public class Potato implements Vagetable{
@Override
public void printName() {
// TODO Auto-generated method stub
System.out.println("patato");
}
}
package com.huang.food.impl;
import com.huang.food.Vagetable;
public class Tomato implements Vagetable{
@Override
public void printName() {
// TODO Auto-generated method stub
System.out.println("tomato");
}
}
然后是工厂类接口和具体实现类:
package com.huang.Factory;
import com.huang.food.Meat;
import com.huang.food.Vagetable;
public interface FoodFactory {
public Meat produceMeat();
public Vagetable produceVagetable();
}
package com.huang.Factory;
import com.huang.food.Meat;
import com.huang.food.Vagetable;
import com.huang.food.impl.Chicken;
import com.huang.food.impl.Tomato;
public class CheapFactory implements FoodFactory{
@Override
public Meat produceMeat() {
// TODO Auto-generated method stub
return new Chicken();
}
@Override
public Vagetable produceVagetable() {
// TODO Auto-generated method stub
return new Tomato();
}
}
package com.huang.Factory;
import com.huang.food.Meat;
import com.huang.food.Vagetable;
import com.huang.food.impl.Beef;
import com.huang.food.impl.Potato;
public class ExpensiveFactory implements FoodFactory{
@Override
public Meat produceMeat() {
// TODO Auto-generated method stub
return new Beef();
}
@Override
public Vagetable produceVagetable() {
// TODO Auto-generated method stub
return new Potato();
}
}
很明显工厂类中同时有生产对应种类实例的方法,通过工厂类的属性(cheap或者expensive),我们很容易知道生产什么实例需要调用哪一个工厂,相较于简单工厂和工厂方法,抽象工厂具备了处理多个抽象属性的功能。
最后是测试类
package com.huang.test;
import com.huang.Factory.CheapFactory;
import com.huang.Factory.ExpensiveFactory;
import com.huang.Factory.FoodFactory;
import com.huang.food.Meat;
import com.huang.food.Vagetable;
public class TestFactory {
public static void main(String[] args) {
FoodFactory factory1 = new CheapFactory();
Meat chicken = factory1.produceMeat();
chicken.printName();
Vagetable tomato = factory1.produceVagetable();
tomato.printName();
FoodFactory factory2 = new ExpensiveFactory();
Meat beef = factory2.produceMeat();
beef.printName();
Vagetable potato = factory2.produceVagetable();
potato.printName();
}
}
虽然三种工厂模式都没能摆脱接口桎梏,但是它们能帮助我们更合理的设计程序,降低功能模块的耦合度。但是还是那句话,伟大的设计模式始终都有它们存在的价值和意义,我们依旧有必要好好理解这些经典的设计模式。