今天复习一下设计模式中的工厂模式,有下面几部分组成
- 工厂模式介绍
- 简单工厂模式
- 工厂方法模式
- 如何选取
- 抽象工厂模式
工厂模式
何为工厂模式?
顾名思义就是一个工厂,只不过这个工厂是用来创建对象的。
为什么需要工厂模式?
工厂是为了客户提供已经完成的产品,所以创建产品对客户是隐藏细节的,试想,如果我们有一个客户需要一台手机,而我们能够提供iphone、华为、三星,那么关系就如下图:
使用伪代码实现如下:
interface Phone{
//TODO
}
class iPhone implements Phone{
//TODO
}
class Huawei implements Phone{
//TODO
}
class Konia implements Phone{
//TODO
}
客户端
class client{
Phone phone;
main(){
if(choose == 华为)
phone = new Huawei();
else if(choose == iPhone)
phone = new iPhone();
else if(choose == 三星)
phone = new Samsung();
}
}
我们会发现,
7. 这个流程高度耦合,一个客户端需要与三部手机都关联。
8. 并且如果客户端要选择哪一款手机都需要知道手机如何生产,也就是客户需要知道一切细节
这往往是我们不想要的,那么根据前人的总结和经验,就产生了——工厂模式。
工厂模式相当于我们新建一个工厂,产品的生产都封装在工厂中,作为客户我们需要什么产品都直接告诉工厂,工厂为我们提供。
下面是更改后的伪代码
class Factory{
Phone phone;
Phone FactoryMethod(String choose){
if(choose == 华为)
phone = new Huawei();
else if(choose == iPhone)
phone = new iPhone();
else if(choose == 三星)
phone = new Samsung();
return phone;
}
}
class Client {
main(){
Factory factory = new factory();
Phone huawei = factory.FactoryMethod("华为");
Phone iPhone= factory.FactoryMethod("iphone");
Phone Samsung= factory.FactoryMethod("三星");
}
}
这样,客户需要什么直接向工厂要就行。注意这是伪代码,具体工厂的实现有许多种,这个下面会介绍
所以工厂模式有以下的好处:
1. 对客户隐藏创建对象的细节
9. 清洗客户端,客户端获取对象不需要那么多选择语句了
10. 客户类只知道哪种类型的对象被创建了,而不必知道哪个具体的子类被初始化了
简单工厂模式
简单工程模式:
- 工厂类中的生产方法为静态方法,也就是类方法
- 工厂类没有子类(一会对比工厂方法模式就懂),所以所有产品都出自一个工厂类
工厂的伪代码如下
class Factory{
Phone phone;
static Phone createProduct(String choose){
if(choose == 华为)
phone = new Huawei();
else if(choose == iPhone)
phone = new iPhone();
else if(choose == 三星)
phone = new Samsung();
return phone;
}
}
重点在于静态的生产方法,也就是类方法,所以客户端不需要实例化工厂
class Client {
main(){
Phone huawei = factory.createProduct("华为");
Phone iPhone= factory.createProduct("iphone");
Phone Samsung= factory.createProduct("三星");
}
}
简单工厂模式优点:
- 工厂类实现具体逻辑
- 客户端不需要产生对象
- 责任分离
简单工厂模式缺点:
如果需要新添加产品,那么需要修改工厂方法,工厂类是不符合开闭原则。所以就有了对应的工厂方法模式
工厂方法模式
工厂方法模式与简单工厂方法不同在于:
- 简单工厂方法中心在于具体的工厂类,而工厂方法模式中心在于抽象的工厂接口
- 简单工厂方法是静态工厂方法,工厂方法模式是动态工厂
- 添加新的产品时工厂方法模式的工厂类不需要改变
- 工厂方法模式支持开闭原则,但是简单工厂方法模式不支持开闭原则体现在工厂类
相同点在于:
两个工厂方法返回类型都是超类类型
下面是图示:
如果我们需要新添加产品,那么只需要新建两个类,一个工厂类,一个产品类。满足对修改关闭,对新增开启。
产品类:
public interface Phone{
abstract public void describe();
}
public class Iphone implements Phone{
@Override
public void describe() {
System.out.println("I am iphone");
}
}
public class Huawei implements Phone{
@Override
public void describe() {
System.out.println("I am huawei");
}
}
工厂类:
public interface Factory {
public Phone getPhone();
}
public class huaweiFactory implements Factory{
@Override
public Phone getPhone() {
return new Huawei();
}
}
public class iphoneFactory implements Factory{
@Override
public Phone getPhone() {
return new Iphone();
}
}
客户端:
public class Client {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String phoneBrand = scanner.nextLine();
scanner.close();
Factory factory = null;
if(phoneBrand.equals("huawei"))
factory = new huaweiFactory();
else if(phoneBrand.equals("iphone"))
factory = new iphoneFactory();
Phone myPhone = factory.getPhone();
myPhone.describe();
}
}
如果新加一个产品,那么只需要新加一个实现抽象产品类的产品类和实现抽象工厂类的工厂类。
比如我们新加一个三星:
就只需要新加两个子类。
public class Samsung implements Phone{
@Override
public void describe() {
System.out.println("I am samsung");
}
}
public class samsungFactory implements Factory{
@Override
public Phone getPhone() {
return new Samsung();
}
}
然而代价就是客户端需要修改,不过如果使用反射就不需要考虑对客户端修改,这一点最后讲
public class Client {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String phoneBrand = scanner.nextLine();
scanner.close();
Factory factory = null;
if(phoneBrand.equals("huawei"))
factory = new huaweiFactory();
else if(phoneBrand.equals("iphone"))
factory = new iphoneFactory();
else if(phoneBrand.equals("samsung"))
factory = new samsungFactory();
Phone myPhone = factory.getPhone();
myPhone.describe();
}
}
这时你可能有疑问,客户端既然需要修改和不使用工厂模式有什么区别
因为工厂是负责实例化我们需要的产品的,生产产品的细节对客户端封装
那么工厂方法模式和简单工厂模式有什么区别呢
简单工厂模式可以看作一个工厂负责所有产品的生产,工厂方法模式可以看作许多不同公司的不同工厂,各司其职,所以工厂方法模式满足开闭原则
如何选择工厂方法模式
如果选择iphone工厂生产三个iPhone。那么如果我们需要把iPhone换成华为,那么只需要把工厂改成华为工厂即可。
Factory factory = new iphoneFactory();
Phone phone1 = factory.getPhone();
Phone phone2 = factory.getPhone();
Phone phone3 = factory.getPhone();
phone1.describe();
phone2.describe();
phone2.describe();
如果此时选择简单工厂模式,那么需要修改的地方就很多
Phone phone1 = FactoryMethod.createPhone("iphone");
Phone phone2 = FactoryMethod.createPhone("iphone");
Phone phone3 = FactoryMethod.createPhone("iphone");
phone1.describe();
phone2.describe();
phone2.describe();
如何选用这两种方法还需要参考他们之间的不同,这一点上面已经介绍需读者自行体会。
反射方法:
直接反射出工厂类
public class Client {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String factoryName = sc.nextLine();
try {
Class factoryClass = Class.forName(factoryName+"Factory");
Factory factory = (Factory) factoryClass.newInstance();
Phone phone = factory.getPhone();
phone.describe();
} catch (Exception e) {
e.printStackTrace();
}
}
}
要记得反射的类一定要跟共产类类名一样
这样就可以根据你所输入的工厂名获取该工厂的产品了。