002 简单工厂
一 .概述
面向对象的第一步是创建对象,毕竟没有对象剩下的都是没用的.
在语法的层面上,java语言给我们构造函数的方式创建对象,但是这种方式却不一定优雅.
我们常常出现的问题就是在一些有关联的对象之中选择一个对象完成任务.
请看到问题的核心:
[1]对象有关联
[2]选择对象,而不是关注于创建对象.
客户端[使用我们代码的人,也可能是我们自己]的目的很简单----就是获得一个对象而已,其它的我们也不想知道.
二 .例子
现在的问题是用户需要获得各种枪,你只要给用户一把用户想要的枪就可以了.
枪的接口:
package com.trek.gun; public interface Gun { void shot(); }
AK47的实现
package com.trek.gun; public class AK47 implements Gun { public void shot() { System.out.println("AK47在射击"); } }
M5的实现
package com.trek.gun; public class M5 implements Gun{ public void shot() { System.out.println("M5在射击"); } }
现在客户端怎么使用枪呢?
package com.trek.gun; public class Client { public static void main(String[] args) { Gun gun = new AK47(); gun.shot(); } }
问题是什么,随着枪的种类变多,客户端就需要管理更多枪的创建.其实客户端只需要一个接口就行了,我给出枪的种类,你给我一把枪就好了.
出现的工厂:
public abstract class GunFactory { public static Gun getGun(String gunName) { if(gunName.equals("ak47")) { return new AK47(); }else if (gunName.equals("M5")) { return new M5(); } throw new IllegalArgumentException("枪的参数输入错误"); } }
那么现在客户端怎么使用嗯?
public class Client { public static void main(String[] args) { Gun gun = GunFactory.getGun("ak47"); gun.shot(); } }
现在客户端只需要通过一个通用的方法就能获得所有的枪了.但是同时出现了两个问题.
[1]客户端需要记住参数是什么,鬼能记住啊.
[2]假如现在加入了B11枪,我们的工厂又变化了啊.[需要加入一个判断条件,开闭原则没了啊]
问题一的解决:
使用枚举解决.
定义一个枚举类:
public enum GunEnum { AK47("ak47"),M5("m5"); private GunEnum(String name) { } }
改造工厂:
public abstract class GunFactory { public static Gun getGun(GunEnum name) { if(name == GunEnum.AK47) { return new AK47(); }else if (name == GunEnum.M5) { return new M5(); } throw new IllegalArgumentException("枪的参数输入错误"); } }
客户端的使用:
public static void main(String[] args) { Gun gun = GunFactory.getGun(GunEnum.AK47); gun.shot(); }
现在用户传参数的时候有了限制了,用户的使用体验提高了.[毕竟用户不能乱传参数了,否则编译都过不去,错误的情况也变少了]
问题二:当枪的种类变多了怎么办啊?
解决方案,反射.
修改我们的枚举类:
public enum GunEnum { AK47("com.trek.gun.AK47"),M5("com.trek.gun.M5"); private String name ; private GunEnum(String name) { this.name= name ; } @Override public String toString() { return this.name; } }
现在我们传递了全类名作为参数.
工厂类:
public abstract class GunFactory { public static Gun getGun(GunEnum name) { try { return (Gun)Class.forName(name.toString()).newInstance(); } catch (Exception e) { e.printStackTrace(); return null; } } }
客户端:
public static void main(String[] args) { Gun gun = GunFactory.getGun(GunEnum.AK47); gun.shot(); }
现在增加一个B11枪:
public class B11 implements Gun { @Override public void shot() { System.out.println("B11 在射击!"); } }
我们的工厂不需要变化,只需要该下枚举类就可以了.[可以保证之前使用的代码不会出问题,因为枚举现在就一个全类名.]
public enum GunEnum { AK47("com.trek.gun.AK47"), M5("com.trek.gun.M5"), B11("com.trek.gun.B11"); private String name ; private GunEnum(String name) { this.name= name ; } @Override public String toString() { return this.name; } }
客户端使用B11,
public static void main(String[] args) { Gun gun = GunFactory.getGun(GunEnum.B11); gun.shot(); }
现在比较完美了,客户端是需要知道一个枪工厂就可以了,参数是有限制的,不会传错的.另外我们增加枪也很方便.
只有一点不爽的地方,那就是枚举类需要不断的修改,其实也很烦人的.
在工作的时候,其实我们一般把全类名放置在配置文件的地方.
总结一下吧:
简单工厂的作用
[1]创建一些有关联的对象,一般是一个继承结构.
[2]简单工厂其实是在选择一个对象而已,至于对象怎么创建,客户端不用去管理.
[3] 工厂获得了创建对象细节的权利.
这个只管重要,我现在可以创建一个单例的对象,带缓存的对象,我随便改,客户端的使用却没什么影响,因为用户根本不知道我在修改.