设为首页 加入收藏

TOP

Java 代码重用:功能与上下文重用(一)
2019-06-05 02:08:03 】 浏览:152
Tags:Java 代码 重用 功能 上下文

我几乎不需要讨论为什么重用代码是有利的。代码重用通常使得程序开发更加快速,并使得 BUG 减少。一旦一段代码被封装和重用,那么只需要检查很少的一段代码即可确保程序的正确性。如果在整个应用程序中只需要在一个地方打开和关闭数据库连接,那么确保连接是否正常则容易的多。但我确信这些你已经都知道了。


有两种类型的重用代码,我称它们为重用类型:


第一种类型是功能重用,这是最常见的一种重用类型。这也是大多数开发人员掌握的一种。即重用一组后续指令来执行某种操作。


第二种类型是上下文重用,即不同功能或操作代码在相同上下文之间,将相同上下文封装为重用代码(这里的上下文指的是一系列相同的操作指令)。虽然它在控制反转中越来越受欢迎但它并不常见。而且,上下文重用并没有被明确的描述,因此它并没有像功能重用一样被系统的使用。我希望你看完这篇文章之后会有所改变。


功能重用是最常见的重用类型。它是一组执行某种操作指令的重用。下面两个方法都是从数据库中读取数据:


对于有经验的开发人员来说,可能很快就能发现可以重用的代码。上面代码中注释“重用代码”的地方是相同的,因此可以封装重用。这些是将用户记录读入用户实例的操作。可以将这些行代码封装到他们自己的方法中,例如:


现在,在上述两种方法中调用readUser()方法(下面示例只显示第一个方法):


readUser()方法也可以在它自己的类中使用修饰符private隐藏。


以上就是关于功能重用的内容。功能重用是将一组执行特定操作的指令通过方法或类封装它们来达到重用的目的。


有时,你希望重用一组操作,但是这些操作在使用的任何地方都不完全相同。例如readAllUsers()readUsersOfStatus()方法都是打开一个连接,准备一条语句,执行它,并循环访问结果集。唯一的区别是readUsersOfStatus()需要在PreparedStatement上设置一个参数。我们可以将所有操作封装到一个readUserList()方法。如下所示:


现在我们从readAllUsers()readUsersOfStatus()调用readUserList(...)方法,并给定不同的操作参数:


我相信你可以找出其他更好的办法来实现重用功能,并将他们参数化使得更加好用。


上下文重用与功能重用略有不同。上下文重用是一系列指令的重用,各种不同的操作总是在这些指令之间进行。换句话说,重复使用各种不同行为之前和之后的语句。因此上下文重用通常会导致控制风格类的反转。上下文重用是重用异常处理,连接和事务生命周期管理,流迭代和关闭以及许多其他常见操作上下文的非常有效的方法。


这里有两个方法都是用 InputStream 做的:


两种方法与流的操作是不同的。但围绕这些操作的上下文是相同的。上下文代码迭代并关闭 InputStream。上述代码中除了使用注释标记的不同之处外都是其上下文代码。


如上所示,上下文涉及到异常处理,并保证在迭代后正确关闭流。一次又一次的编写这样的错误处理和资源释放代码是很繁琐且容易出错的。错误处理和正确的连接处理在 JDBC 事务中更加复杂。编写一次代码并在任何地方重复使用显然会比较容易。


幸运的是,封装上下文的方法很简单。 创建一个上下文类,并将公共上下文放入其中。 在上下文的使用中,将不同的操作指令抽象到操作接口之中,然后将每个操作封装在实现该操作接口的类中(这里称之为操作类),只需要将该操作类的实例插入到上下文中即可。可以通过将操作类的实例作为参数传递给上下文对象的构造函数,或者通过将操作类的实例作为参数传递给上下文的具体执行方法来完成。


下面展示了如何将上述示例分隔为上下文和操作接口。StreamProcessor(操作接口)作为参数传递给StreamProcessorContextprocessStream()方法。


现在可以像下面示例一样使用StreamProcessorContext类打印出流内容:


或者像下面这样读取输入流内容并添加到一个字符序列中:


正如你所看到的,通过插入不同的StreamProcessor接口实现来对流做任何操作。一旦StreamProcessorContext被完全实现,你将永远不会有关于未关闭流的困扰。


上下文重用非常强大,可以在流处理之外的许多其他环境中使用。一个明显的用例是正确处理数据库连接和事务(open - process - commit()/rollback() - close())。其他用例是 NIO 通道处理和临界区中的线程同步(lock() - access shared resource - unlock())。它也能将API的已检查异常转换为未检查异常。


当你在自己的项目中查找适合上下文重用的代码时,请查找以下操作模式:


当你找到这样的模式时,前后的常规操作就可能实现上下文重用。


有时候你会希望在上下文中有多个插件点。如果上下文由许多较小的步骤组成,并且你希望上下文的每个步骤都可以自定义,则可以将上下文实现为模板方法。模板方法是一种 GOF 设计模式。基本上,模板方法将算法或协议分成一系列步骤。一个模板方法通常作为一个单一的基类实现,并为算法或协议中的每一步提供一个方法。要自定义任何步骤,只需创建一个扩展模板方法基类的类,并重写要自定义的步骤的方法。


下面的示例是作为模板方法实现的 JdbcContext。子类可以重写连接的打开和关闭, 以提供自定义行为。必须始终重写processRecord(ResultSet result)方法, 因为它是抽象的。此方法提供不属于上下文的操作,在使用JdbcContext的不同情况下的操作都不相同。这个例子不是一个完美的JdbcContext。它仅用于演示在实现上下文时如何使用模板方法。


这是扩展 JdbcContext 以读取用户列表的子类:


下面是如何使用 ReadUsers 类:


如果ReadUsers类需要从连接池获取连接并在使用后将其释放回该连接池,则可以通过重写openConnection()closeConnection(Connection connection)方法来插入该连接。


注意如何通过方法重写插入操作代码。JdbcContext的子类重写processRecord方法以提供特殊的记录处理。 在StreamContext示例中,操作代码封装在单独的对象中,并作为方法参数提供。实现操作接口StreamProcessor的对象作为参数传递给StreamContext类的processStream(...)方法。


实施上下文时,你可以使用这两种技术。JdbcContext类可以将实现操作接口的ConnectionOpenerConnectionCloser对象作为参数传递给execute方法,或作为构造函数的参数。就我个人而言,我更喜欢使用单独的操作对象和操作接口,原因有两个。首先,它使得操作代码可以更容易单独进行单元测试;其次,它使得操作代码在多个上下文中可重用。当然,操作代码也可以在代码中的多个位置使用,但这只是一个优势。毕竟,在这里我们只是试图重用上下文,而不是重用操作。


现在你已经看到了两种不同的重用代码的方法。经典的功能重用和不太常见的上下文重用。希望上下文的重用会像功能重用一样普遍。上下文重用是一种非常有用的方法,可以从 API 的底层细节(例如JDBC,IO 或 NIO API等

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Python使用MyQR制作专属动态彩色.. 下一篇Java 动态代理模式浅析

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目