我在JavaWorld上找到了this article,其中Allen Holub解释了Getters / Setters的替代方法,它保留了隐藏对象实现的原则(他的示例代码也可以在下面找到)。
我们解释说,Name
/ EmployeeId
/ Money
类应该有一个构造函数,只需要一个字符串 - 如果你将它键入int
,以后需要将其更改为long
,您将不得不修改该类的所有用途,并且您不必使用此模式。
我想知道:这不是简单地将问题转移到解析被抛出的String
参数吗?例如,如果使用EmployeeId
的所有代码(从Exporter
收到)将String
解析为int
,则会突然开始导出long
值,你需要修改完全相同的用途......如果你开始将其解析为long
,它可能必须更改为double
(即使这对id没有意义)。 ..如果您无法确定将String
解析为什么,则无法实现任何内容。
除了这个问题,我还有另一个问题:我意识到这篇文章已有七年多了,所以有人能指出我最近关于OO设计的一些概述,特别是关于getter / setter和实现隐藏辩论的想法吗?
public class Employee
{ private Name name;
private EmployeeId id;
private Money salary;
public interface Exporter
{ void addName ( String name );
void addID ( String id );
void addSalary ( String salary );
}
public interface Importer
{ String provideName();
String provideID();
String provideSalary();
void open();
void close();
}
public Employee( Importer builder )
{ builder.open();
this.name = new Name ( builder.provideName() );
this.id = new EmployeeId( builder.provideID() );
this.salary = new Money ( builder.provideSalary(),
new Locale("en", "US") );
builder.close();
}
public void export( Exporter builder )
{ builder.addName ( name.toString() );
builder.addID ( id.toString() );
builder.addSalary( salary.toString() );
}
//...
}
答案 0 :(得分:10)
问题1 :
字符串解析看起来很奇怪。恕我直言,你只能做很多事情来预测未来的增强功能。您可以从一开始就使用long
参数来确定,或者考虑稍后添加其他构造函数。或者,您可以引入可扩展的参数类。见下文。
问题2 : 有几种情况可以使用构建器模式。
复杂对象创建
当您处理具有大量属性的非常复杂的对象时 你最好只在对象创建时设置一次,这样做 常规构造函数可能变得难以阅读,因为构造函数会 有一长串参数。将其作为API发布并不是一种好的风格 因为每个人都必须仔细阅读文档并确保 他们不会混淆参数。
相反,当你提供建筑师时,只有你必须应对(私人) 获取所有参数的构造函数,但是您的类的消费者可以 使用更易读的个别方法。
Setter不是一回事,因为它们会允许你改变对象 创建后的属性。
可扩展API
当您只为您的班级及以后发布多参数构造函数时 决定您需要添加一个新的(可选)属性(比如在软件的更高版本中) 你必须创建一个与第一个相同的第二个构造函数,但是 再拿一个参数。否则 - 如果你只是将它添加到现有的 构造函数 - 您将破坏与现有代码的兼容性。
使用构建器,您只需为新属性添加一个新方法,所有方法都存在 代码仍然兼容。
不变性
软件开发趋向于并行执行 多线程。在这种情况下,最好使用不能的对象 在创建它们之后进行修改(不可变对象),因为这些 不会导致来自多个线程的并发更新问题。这是 为什么不能选择安装人员。
现在,如果你想避免多参数公共构造函数的问题, 这使得建设者成为一种非常方便的选择。
可读性(“Fluent API”)
如果构建器的方法是,基于构建器的API可以非常容易阅读 巧妙地命名,您可以使用几乎像英语句子的代码。
通常,构建器是一种有用的模式,并且根据您使用的语言,它们要么非常容易使用(例如Groovy),要么对于API的提供者来说更乏味(例如在Java中)。然而,对于消费者来说,他们可以同样轻松。
答案 1 :(得分:2)
您可以更简洁的方式实现Builders。 ;)我经常发现手工编写建设者是单调乏味且容易出错的。
如果您有一个生成数据值对象及其构建器(和编组器)的数据模型,它可以很好地工作。在这种情况下,我相信使用Builders是值得的。
答案 2 :(得分:2)
构造函数有很多问题需要参数(例如,你不能在几个步骤中构建对象)。此外,如果您需要大量参数,最终会对参数顺序感到困惑。
最新的想法是使用“fluent interface”。它适用于返回this
的setter。通常,方法名称中省略set
。现在你可以写:
User user = new User()
.firstName( "John" )
.familyName( "Doe" )
.address( address1 )
.address( address2 )
;
这有几个好处:
address
)。主要缺点是,当实例“准备好”使用时,您不再知道。
解决方案是进行多次单元测试或专门添加“init()”或“done()”方法,该方法执行所有检查并设置标记“此实例已正确初始化”。
另一个解决方案是在build()
方法中创建实际实例的工厂,该方法必须是链中的最后一个:
User user = new UserFactory()
.firstName( "John" )
.familyName( "Doe" )
.address( address1 )
.address( address2 )
.build()
;
Groovy等现代语言将此转换为语言功能:
User user = new User( firstName: 'John', familyName: 'Doe',
address: [ address1, address2 ] )
答案 3 :(得分:0)
当您需要对象的构造函数(以类似方式考虑工厂)时,使用对象强制代码将基本要求传递给构造函数。越明显越好。 您可以使用setter保留稍后(注入)的可选字段。