简述 实现对客户端中对象族的平替 。
对象族 具有共同主题的一组对象的集合。比如,华为的手机,笔记本,平板可以统称为华为族。
我们借以下案例来说说如何使用抽象工厂模式平替对象族。
优化案例 最初版 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public interface Uploader { void upload (String fileName) ; } public interface Downloader { void download (String fileName) ; } public class LinuxUploader implements Uploader { public void upload (String fileName) { System.out.printf("[Linux]正在上传%s..." , fileName); } } public class LinuxDownloader implements Downloader { public void download (String fileName) { System.out.printf("[Linux]正在下载%s..." , fileName); } } public class UnixUploader implements Uploader { public void upload (String fileName) { System.out.printf("[Unix]正在上传%s..." , fileName); } } public class UnixDownloader implements Downloader { public void download (String fileName) { System.out.printf("[Unix]正在下载%s..." , fileName); } }
客户端调用如下。
1 2 3 4 5 6 public class Client { public static void main (String[] args) { Uploader uploader = new LinuxUploader (); Downloader downloader = new LinuxDownloader (); } }
传统是new创建对象的方式有着硬编码的问题。当我们需要把所有LinuxXXX对象改为UnixXXX对象时,就必须在项目中检索所有的LinuxXXX修改为UnixXXX。这无疑增加了大量的无意义的工作。
修改版v1(简单工厂) 增加一个工厂类,其他不变。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class Factory { static Uploader uploader (String target) { if ("LinuxUploader" .equals(target)) { return new LinuxUploader (); } else if ("UnixUploader" .equals(target)) { return new UnixUploader (); } throw new Exception ("输入的参数错误" ); } static Downloader downloader (String target) { if ("LinuxDownloader" .equals(target)) { return new LinuxDownloader (); } else if ("UnixDownloader" .equals(target)) { return new UnixDownloader (); } throw new Exception ("输入的参数错误" ); } }
修改后,客户端的代码调用。
1 2 3 4 5 6 7 8 public class Client { public static void main (String[] args) { Uploader up1 = Factory.instance("LinuxUploader" ); Downloader down1 = Factory.instance("LinuxDownloader" ); Uploader up2 = Factory.instance("UnixUploader" ); Downloader down2 = Factory.instance("UnixDownloader" ); } }
在一定程度上解决了客户端硬编码问题。并且当我们需要把所有LinuxUploader对象改为UnixUploader对象时,只需要在Factory中将new LinuxUploader() → new UnixUploader()
即可。这无疑节省了很多的时间,也无需为硬编码带来的大量改修而苦恼。
但是目前这个优化方案依然有至少两个问题,一是Factory.uploader
方法中耦合了所有的Uploader实现类,这可能有碍于未来的项目维护,二是new LinuxUploader() → new UnixUploader()
这种修改方式会导致代码目的不明确,既然不论是LinuxUploader还是UnixUploader都直接生成UnixUploader对象,就没有必要定义LinuxUploader了呀。实际上是因为客户端代码中还有使用OSFactory.instance("LinuxUploader")
来创建的对象,为了不修改客户端代码,强行做如上修改。
修改版v2(抽象工厂) 将原本的工厂类抽象化,并定义一系列不同的实现类,其余不变。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public interface Factory { Uploader uploader () ; Downloader downloader () ; } public class LinuxFactory { public Uploader uploader () { return new LinuxUploader (); } public Downloader downloader () { return new LinuxDownloader (); } } public class UnixFactory { public Uploader uploader () { return new UnixUploader (); } public Downloader downloader () { return new UnixDownloader (); } }
修改后,客户端的代码调用。
1 2 3 4 5 6 7 8 public class Client { private static Factory factory = new LinuxFactory (); public static void main (String[] args) { Uploader uploader = factory.uploader(); Downloader downloader = factory.downloader(); } }
将原本Factory
类中臃肿的逻辑分散到各个子类中,提高了系统的可维护性,不用再每次都修改Factory
类了。
那么,问题来了,这样的结构对于我们的项目有什么帮助吗?有,而且很大,在客户端定义了一个静态的属性factory
,当接下来客户换了系统从Linux换到了Unix,那我们也需要更换对应的上传下载的类,这时我们只要修改factory引用的具体工厂类的对象就可以了,很方便。实际上还可以更加方便的实现对象族的平替。而为了实现这个需求,我们需要结合Java反射这项技术。请看下面的代码。
修改版v3(抽象工厂+反射) 只修改客户端的调用方式,其他位置不做修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class Client { private static Factory factory; static { Properties prop = new Properties (); FileReader fileReader = new FileReader ("src/resource/props/config.prop" ); prop.load(fileReader); fileReader.close(); factory = (Factory) Class.forName(prop.getProperty("FACTORY" )) .getDeclaredConstructor().newInstance(); } public static void main (String[] args) { Uploader uploader = factory.uploader(); Downloader downloader = factory.downloader(); } }
增加一个properties文件文件,定义如下。
1 2 FACTORY =design.abstractfactory.LinuxFactory
当需要将系统中的Linux
系列的对象转化为Unix
系列时,只需要更改上述配置文件即可,具体如下。
1 2 FACTORY =design.abstractfactory.UnixFactory
总之就是非常方便。
总结 优点
缺点
应用场景