在实际项目设计过程中,接口是一个重要组成元素,不同层之间的操作需要通过接口来调用。利用接口,可以实现子类的隐藏,也可以更好地描述不同层之间的操作标准。Java 中要想获得接口对象,需要通过关键字 new 来实现。
【示例 1】通过关键字new实例化接口对象
1. package demo;
2. interface IMessage { // 定义业务接口
3. /**
4. * 信息回显处理
5. * @param msg 原始信息
6. * @return 追加回显后的信息
7. */
8. public String echo(String msg);
9. }
10. class MessageImpl implements IMessage { // 定义接口子类
11. @Override
12. public String echo(String msg) {
13. return "【ECHO】" + msg; // 回显处理
14. }
15. }
16. public class TestMessageDemo {
17. public static void main(String[] args) {
18. IMessage message = new MessageImpl(); // 通过关键字new直接实例化
19. System.out.println(message.echo("https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=Mzg3MzczNjYzMA==&scene=124&uin=&key=&devicetype=Windows+10+x64&version=6302019c&lang=zh_CN&a8scene=7&fontgear=2"));
20. }
21. }
程序执行后,结果如下:
【ECHO】https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=Mzg3MzczNjYzMA==&scene=124&uin=&key=&devicetype=Windows+10+x64&version=6302019c&lang=zh_CN&a8scene=7&fontgear=2
本程序是一个典型的 Java 原生代码处理操作。new 是 Java 进行对象实例化最基础的关键字,但使用 new 会暴露 IMessage 接口子类,同时会让子类与接口之间产生耦合,如图 1 所示。
图 1 通过关键字 new 实例化接口对象
很明显,不同层之间出现接口子类暴露是一种不合理的设计模式,因为调用者并不需要知道具体子类是哪个,只需要取得接口对象即可。采用工厂设计模式,就可以起到解耦合的目的。在此我向大家推荐一个架构学习交流圈。交流学习伪鑫:1253431195(里面有大量的面试题及答案)里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多
【示例 2】通过工厂设计模式获取接口对象
1. package demo;
2. interface IMessage { // 定义业务接口
3. /**
4. * 信息回显处理
5. * @param msg 原始信息
6. * @return 追加回显后的信息
7. */
8. public String echo(String msg);
9. }
10. class Factory {
11. private Factory() {} // 构造方法私有化
12. public static IMessage getInstance(String className) {
13. if ("echo".equals(className)) {
14. return new MessageImpl() ; // 返回接口实例
15. }
16. return null ;
17. }
18. }
19. class MessageImpl implements IMessage { // 定义接口子类
20. @Override
21. public String echo(String msg) {
22. return "【ECHO】" + msg; // 回显处理
23. }
24. }
25. public class TestMessageDemo {
26. public static void main(String[] args) {
27. IMessage message = Factory.getInstance("echo") ; // 通过工厂类获取接口对象
28. System.out.println(message.echo("https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=Mzg3MzczNjYzMA==&scene=124&uin=&key=&devicetype=Windows+10+x64&version=6302019c&lang=zh_CN&a8scene=7&fontgear=2"));
29. }
30. }
程序运行后,结果如下:
【ECHO】https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=Mzg3MzczNjYzMA==&scene=124&uin=&key=&devicetype=Windows+10+x64&version=6302019c&lang=zh_CN&a8scene=7&fontgear=2
本程序采用工厂设计模式解决了调用者与具体子类之间的耦合关联。调用者只需通过 Factory 类,就可以获取接口对象,不用再关注具体的子类是哪一个。本程序的代码结构如图 2 所示。
图 2 通过工厂类获取接口对象
虽然通过工厂设计模式可以改善接口对象的获取处理,但如果一个接口有无数个子类,且这些子类可随时动态增加,这样的静态工厂设计就会产生问题,会导致大量的修改操作。实际开发中,还需要结合反射机制来改善工厂设计。
【示例 3】利用反射机制改善工厂设计
1. package demo;
2. interface IMessage { // 定义业务接口
3. /**
4. * 信息回显处理
5. * @param msg 原始信息
6. * @return 追加回显后的信息
7. */
8. public String echo(String msg);
9. }
10. class Factory {
11. private Factory() {} // 构造方法私有化
12. @SuppressWarnings("unchecked")
13. public static T getInstance(String className) { // 通过反射实例化
14. try {
15. return (T) Class.forName(className).getDeclaredConstructor().newInstance();
16. } catch (Exception e) {
17. return null ;
18. }
19. }
20. }
21. class MessageImpl implements IMessage { // 定义接口子类
22. @Override
23. public String echo(String msg) {
24. return "【ECHO】" + msg; // 回显处理
25. }
26. }
27. public class TestMessageDemo {
28. public static void main(String[] args) {
29. IMessage message = Factory.getInstance("demo.MessageImpl") ; // 通过工厂类获取接口对象
30. System.out.println(message.echo("https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=Mzg3MzczNjYzMA==&scene=124&uin=&key=&devicetype=Windows+10+x64&version=6302019c&lang=zh_CN&a8scene=7&fontgear=2"));
31. }
32. }
程序运行后,结果如下:
【ECHO】https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=Mzg3MzczNjYzMA==&scene=124&uin=&key=&devicetype=Windows+10+x64&version=6302019c&lang=zh_CN&a8scene=7&fontgear=2
本程序通过反射机制修改了 Factory 工厂类的设计,在调用 Factory.getInstance( ) 方法时,必须明确传入子类对象的完整名称。这样设计的好处在于,工厂类不会再与某个具体的接口或子类耦合,因此更加具有通用性。本程序的代码结构如图 3 所示。
图 3 反射与工厂设计相结合
本例利用反射机制成功改良了工厂类的设计,使得程序结构更加清晰,同时避免了程序代码可能产生的耦合问题,但这样的配置依然存在着以下问题:
- 获取对象时需要传递完整的“包.类”名称。这样的客户端调用显然存在缺陷,最好的解决方案是追加一个配置文件,而后根据某个名称来获取对应的“包.类”名称信息,再通过反射进行加载。
- 配置文件不应该只简单描述“名称 = 包.类”关系,还应包含依赖关系配置。
- 应该更合理地实现多线程管理,避免过多的重复对象产生。
也就是说,要想实现一整套合理的对象管理容器,直接采用原生 Java 代码并不只靠一个简单的反射机制可以解决问题,还需要考虑各种对象的状态与对象管理。
Spring 的 IOC 技术可以帮助开发者减少这些设计上的思考,使其将更多精力放在核心代码的开发处理上。