OO设计建议 – toString

所以我得到了Address类:

 class Address { private String streetAddress; private int number; private String postalCode; private City city; private State state; private Country country; } 

我想让它的可读版本显示在网格列中。

什么是实现这一目标的最佳和最简洁的方法?

  1. Address内的toString方法( 我个人不喜欢这种方法,因为’toString’与地址没有直接关系
  2. class ReadableAddressFormatter
    • ReadableAddressFormatterAddress addressToFormat
    • public String getFormatted()
  3. 以前的类,但getFormmated将是静态的,接收Address实例并返回字符串
  4. 其他? 建议请。

我正在寻找一个好的设计,专注于清洁代码解耦可维护性

所有这些方法都已被使用,并且没有办法提供“与上下文无关”的最佳实践。 软件工程的最佳答案通常是“它取决于”。 那就是说,让我们分析一下:

  1. KISS方法最好。 我这样做是为了我所有的基本“打印到控制台,确保一切正常”。 如果你有一个特定的格式,你可以期待地址,这是低悬的果实/轻松获胜的解决方案。 您可以始终覆盖此项,或在一次性情况下以不同方式打印对象。
  2. 这是最具扩展性的解决方案,因为它很好地允许本地化和自定义格式化。 是否合适取决于您希望以不同格式显示地址的频率。 你是否真的需要死星来摧毁苍蝇,或者是否能够改变为全部大写或者在对应用程序至关重要的语言之间进行更改?
  3. 我不会建议这种方法,因为它通常开始将“视图级别”逻辑渗透到域中,这通常最好由其他层处理(在类MVC方法中)。 有人可能认为toString()做同样的事情,但toString()也可以被认为是对象如何出现在外部世界的“名称”或“本质”,所以我要说它不仅仅是表现。

希望这有助于从一开始就考虑清洁代码,解耦和可维护性而感到荣幸。

关于原则#2的实例 – 使用策略模式 ,遵守单一责任原则 , 开放/封闭原则并允许通过dependency injection进行控制反转 – 比较以下方法(由@SteveJ慷慨提供) :

 public class Address { private String streetAddress; private int number; private String postalCode; private String city; private String state; private String country; public String toLongFormat(){ return null; // stitch together your long format } public String toShortFormat(){ return null; // stitch together your short format } public String toMailingLabelFormat(){ return null; // stitch together your mailing label format } @Override public String toString(){ return toShortFormat(); // your default format } } } 

有了这个(在“大多数正确的”Groovy中):

 public interface AddressFormatter { String format(Address toFormat) } public class LongAddressFormatter implements AddressFormatter { @Override public String format(Address toFormat){ return String.format("%sBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAH%n%s", toFormat.streetAddress, toFormat.postalCode) } } public class ShortAddressFormatter implements AddressFormatter { @Override public String format(Address toFormat){ return String.format("%d", toFormat.number) } } public class Address { private String streetAddress; private int number; private String postalCode; private String city; private String state; private String country; public AddressFormatter formatter = new ShortAddressFormatter(); // just to avoid NPE public void setFormatter(AddressFormatter fr) { this.formatter = fr; } @Override public String toString(){ return formatter.format(this); // your default format } } def addrr = new Address(streetAddress:"1234 fun drive", postalCode:"11223", number:1) addr.setFormatter(new LongAddressFormatter()); println "The address is ${addrr}" addr.setFormatter(new ShortAddressFormatter()); println "The address is ${addrr}" 

正如@SteveJ所观察到的:

“所以你有不同的格式”策略“,你可以在它们之间切换……我有这个想法,你会设置格式一次,并坚持下去…如果你想添加另一种格式风格,你不要不必打开并重写地址类,但要编写一个新的单独样式,并在想要使用时注入它。“

.NET解决方案:

重写Object.ToString()似乎是最合理的解决方案。 这样可以在以下情况下使用它: Console.WriteLine("Home Address: {0}", homeAddress);

如果您希望提供其他格式 ,Address类应实现IFormattable

此外,您应该创建一个从IFormatProviderICustomFormatter实现的AddressFormatter类。

MSDN链接提供了非常好的放置示例(BinaryFormatter和AcctNumberFormat),但如果这些还不够,还要看一下这个好例子: PhoneFormatter


另外,如果您决定全力以赴并实现IFormattable和自定义IFormatProvider / ICustomFormatter,那么我建议让ToString()简单地使用默认提供程序调用ToString(String格式,IFormatProvider formatProvider)。 这样你就可以解决本地化和地址类型(短,长等)的问题。

使用toString在函数本身之外不需要额外的行李; 似乎是最简单的解决方案。 这是有原因的,对吗?

通常我从数据层划分表示层。 将它呈现给GUI在我看来与表示层有关,而不是数据层。

我建议你在表示层的某处放一个函数,将地址转换为字符串。

数据的呈现与数据无关!

静态方法很好。 转换器类会更好,你可以为你的应用程序保留一个单独的实例,但如果你将应用程序从GUI移动到另一种格式的WEB,或者如果在一个窗口中你想显示所有内容,你可以替换它或写另一个实例另一个窗口,您只想显示以其他方式格式化的部分信息或信息。

您可以遵循几种模型,例如Microsoft WPF使用完全另一种方法,MVVM,模型视图视图模型,它将允许您从表示层划分业务逻辑中的数据层。

我通常在C#中覆盖ToString,在java中覆盖toString仅用于调试目的(提供可用于调试的字符串)或用于某种简单序列化到字符串,通常还在java中添加FromString(或fromString方法)。 一个例子是自定义类型,如Point,Vector,Matrix等。

谈论C#世界..

 public class AddressToStringConverter { public virtual string ToString(Address address) { return address.Street + ", " + address.City } } 

然后在你的表格(例如)。

 AddressToStringConverter myConverter = new AddressToStringConverter(); public Address CurrentSelectedAddress { get { ... } } public button1_click(object sender, EventArgs e) { button1.Text = myConverter.Convert(address); } 

如果您愿意,可以使用其他有用的接口,例如ITypeConverter

toString()是最灵活和方便的,在将Address类的对象与String组合时会隐式调用,如System.out.println(“我的地址是”+ objectOfAddressClass)。

我可以想到不覆盖toString()的唯一原因是你需要改变格式。 然后你需要不同的方法(如toMailingString()和toShortFormString()等)或参数化方法(如toMailingString(boolean useShortForm)或其他),但无论如何,toString()都不会删除它。

当然,你可以(而且应该)做到这两点。 将toString()作为默认值,可能调用一种特定的格式方法,然后使用其他辅助方法替换格式。

 public class TestClass { class City{}; class State{}; class Country{}; class Address { private String streetAddress; private int number; private String postalCode; private City city; private State state; private Country country; public String toLongFormat(){ return null; // stitch together your long format } public String toShortFormat(){ return null; // stitch together your short format } public String toMailingLabelFormat(){ return null; // stitch together your mailing label format } @Override public String toString(){ return toShortFormat(); // your default format } } } 

你用Java标记了你的post,所以我会回答Java(更具体地说是Swing)。 此任务通常是特定TableCellRenderer的任务。 如果必须为其他可视组件使用相同的格式,我确实会在可实例化的类中提取格式(解决方案2)。 这将允许子类在需要时自定义格式。

我认为返回字符串的toString()方法是最好的方法。 如果你有一个Address实例,让我们说address ,那么很明显address.toString()作用。 toString()Address没有直接关联的事实并没有真正改变任何东西。