在我的一些方法中,存在太多参数,并且很难维护和读取源代码.有时候我担心的问题是" 他们是否以适当的顺序传递了适当的价值? "
我使用Checkstyle作为我的Eclipse插件,它为我提供了超过7个参数的警告.
我不确定它可能只是一个编码标准,我不关心它.但是当通过视图,服务或dao传递许多参数时,我注意到它很难阅读并且以后很难修改.
所以,我试图通过这些参数...
一些对象或豆类.但这给了我另一个问题,因为我的参数不能得到任何保证(不确定它们是否存在).
HashMap类型参数.但这可能会迫使我检查一些验证并尝试匹配方法调用方面的密钥.
以上两种方法也可能会丢失编译时错误检查.有没有减少参数计数的建议?
传递HashMap是非类型化脚本语言中的常见做法,但在Java中却是一种不好的做法.它击败了强类型的优势,这是您在Java中获得生产力的一部分.换句话说,Java编译器将无法帮助您在开发过程中发现错误,并且您更有可能在运行时捕获它们.
如果您传递的参数在概念上是相关的,那么您可以在提及时将它们分组到适当的对象中.例如,如果您传递诸如firstName,lastName,dateOfBirth等参数,则可以传递具有这些属性的Person对象.这使用OO使您的设计更容易思考和维护.
如果我理解你对此的意思:"但是这给我带来了另一个麻烦,因为我的参数不能得到任何保证(不确定是否包含)",你可以强制执行你的Person或者等对象所需的保证.实例化.一种方法是使用不可变的Person(等)对象:没有setter,而是通过构造函数传递所有params.如果它们不正确,则抛出IllegalArgumentException.
祝好运!
有一些技术可以减少参数的数量;
使用最小化的方法(将方法分解为多个方法,每个方法只需要一部分参数)
使用实用程序类(帮助程序类)来保存参数组(通常是静态成员类)
使Builder模式从对象构造适应方法调用.
尝试使用更好的架构设计减少单独包之间的数据流.
参考一些标准的java书;
Java:如何编程
首先是Java
有效的Java
还尝试学习设计模式,它作为最佳编码实践非常有用.
首先设计模式
在我提出我的提议之前,让我先谈谈提出的建议.我冒昧地跳过"臭"解决方案--HashMap和bean,因为你可以清楚地看到它们的缺点.
盲目地使用辅助类来保存参数组.当团队凝聚力时(例如Mark Phillips的回答),他们可以真正发光,但是否则会导致问题(基本上就像打字的HashMap一样).我怀疑它们是否适用于将7个参数从视图传递到DAO层的问题.
当它们有意义时,最小化的方法也很棒(如在Effective Java book中的List示例中).我很少看到他们这样做的地方,所以我怀疑他们会解决你的问题.
Builder模式通常非常干净,但它只解决了一层的问题 - 它没有告诉你如何进一步传递参数.当你从视图中获得一个参数,这在DAO中是必需的,那么Builder只会膨胀你的代码,你仍然需要传递参数.
在我最终提出解决方案之前,让我挑战一个常见的隐含假设,即所有数据都需要以方法参数的形式通过堆栈传递.只有将处理对象应用于应用程序或会话范围时,才会出现这种情况.在请求处理开始时创建所有相关对象时,约束消失.然后,您可以使用对象的构造函数仅向其传递必要的信息.
在某种程度上,这类似于方法对象或命令模式.
为了应用此解决方案,您需要更改系统的入口点 - 通常这是某个视图层对象中的方法.让它负责两件事:
请求对象图创建
调用对象图root的执行/运行方法
第一步至关重要.这是您从每个层构建请求范围对象的位置:视图,服务和DAO.对于每个对象,您只需将所需数据传递给其构造函数(例如,如果仅在DAO中需要参数"userIP" - 例如,用于审核DB访问,则仅将其传递给DAO请求对象).请求对象还需要对其协作者的引用(如需要DAO的服务) - 相应地通过构造函数传递它们.
第二步:当您设置对象图时,只需在第一个上调用execute/run方法(通常是视图层中的对象).
/** The example (in Scala) shows how your app's entry point could look like. * The presented method belongs to an app-scoped view-layer object. */ def delete(itemId: Id, userIP: IPAddress) { // Note, that only RepositoryHelperReq class is interested in the // "itemId" and "userIP" parameters val repoReq = MainRepositoryReq(RepositoryHelperReq(itemId, userIP)) val serviceReq = MainServiceReq(ServiceHelperReq(repoReq)) val viewReq = MainViewReq(ViewHelperReq(serviceReq)) viewReq.execute() }
现在让我回应一些对这种模式的预期批评.
有人会说,性能会受到影响,因为堆上会有更多的对象来进行垃圾收集.我会问那些测量,因为通常它不是对象分配,而是成本性能,而是对象保留(参见Simon Ritter的最后一个演示).
有些人会询问应用程序或会话范围的数据,例如数据源或购物篮对象.这些对象仍然可以使用 - 您只需将它们注入到请求范围的对象中.
有些人会批评依赖结构,认为视图应仅依赖于服务而不依赖于DAO.这是有效的评论,只需注意,在经典的webapps中,你仍然有一个中心位置,这取决于所使用的每一层(通常被称为"世界末日").有时它是web.xml,有时它是Spring应用程序上下文或Guice模块.如果您关心正确的依赖关系,我建议您将所有工厂逻辑放在这样的位置,让它实现一些View-layer接口并注入视图.这样你的整个依赖结构将保持干净和模块化.
有人会说,流行的DI框架(主要是Spring)支持这种模式非常糟糕.这是真的,你需要使用一个像样的DI库(Guice,Dagger for Java或Macwire,如果你喜欢Scala)或准备对抗Spring以便做到正确.
没有长参数列表的气味
没有不连贯的"请求上下文"对象(MagicContainer反模式)
没有"印章" - 耦合 - 中间层不需要依赖于传递的参数,因此它们可以独立发布,更可测试和更清洁
数据仅在需要时使用 - 更容易测试,更少模拟
即使在其他方法失败的情况下也可以使用,例如当您没有内聚的ParameterObject来提取时,或者当您无法轻松地将方法拆分为最小的正交方法时
积分
MiškoHevery在他的优秀博客文章" 如何使用Servlet完成所有错误"中精确地确定了管理对象生命周期(请参阅"更常见的违规"片段).我想感谢他的帖子,因为很难在其他来源找到关于这个具体问题的准确指导.