简体   繁体   English

OO设计建议 - toString

[英]OO Design Advice - toString

So I got the Address class: 所以我得到了Address类:

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

And I want to get its readable version to, lets say, show in a grid column. 我想让它的可读版本显示在网格列中。

Whats the best and concise way to implement this? 什么是实现这一目标的最佳和最简洁的方法?

  1. toString method inside class Address ( I personally don't like this approach, as 'toString' is not directly related to an Address ) Address内的toString方法( 我个人不喜欢这种方法,因为'toString'与地址没有直接关系
  2. class ReadableAddressFormatter class ReadableAddressFormatter
    • ReadableAddressFormatter ( Address addressToFormat ) ReadableAddressFormatterAddress addressToFormat
    • public String getFormatted() public String getFormatted()
  3. Previous class but getFormmated would be static, receiving the Address instance and returning the string 以前的类,但getFormmated将是静态的,接收Address实例并返回字符串
  4. Other? 其他? Suggestions please. 建议请。

I'm looking for a good design, focusing also in Clean Code , Decoupling and Maintainability . 我正在寻找一个好的设计,专注于清洁代码解耦可维护性

All of these methods have been used, and there's no way to offer a "context independent" best practice. 所有这些方法都已被使用,并且没有办法提供“与上下文无关”的最佳实践。 The best answer in Software Engineering is usually "it depends." 软件工程的最佳答案通常是“它取决于”。 That's said, let's analyze each: 那就是说,让我们分析一下:

  1. The KISS approach at its finest. 最好的KISS方法。 I do this for all my basic "print to console, make sure things are working" kind of thing. 我这样做是为了我所有的基本“打印到控制台,确保一切正常”的东西。 If you have a specific format you can expect for addresses, this is the low-hanging fruit/easy win solution. 如果你有一个特定的格式,你可以期待地址,这是一个低悬的果实/轻松获胜的解决方案。 You can always override this or print out the object differently in one off situations. 您可以始终覆盖此项,或在一次性情况下以不同方式打印对象。
  2. This is the most extensible solution, in that it will nicely allow for localization and custom formatting. 这是最具扩展性的解决方案,因为它很好地允许本地化和自定义格式化。 Whether it is appropriate depends on how often you expect addresses to be shown in different formats. 是否合适取决于您希望以不同格式显示地址的频率。 Do you really need that Death Star to knock out a fly, or is the ability to change to all uppercase or change between languages pivotal to your app? 你真的需要那个死星来摧毁一只苍蝇,还是能够改变为全部大写或改变你的应用程序的关键语言?
  3. I would not suggest this approach, as it generally started to bleed "view level" logic into the Domain, which is usually best handled by other tiers (in a class MVC approach). 我不会建议这种方法,因为它通常开始将“视图级别”逻辑渗透到域中,这通常最好由其他层处理(在类MVC方法中)。 One could argue that toString() does the same thing, but toString() can also be thought of as the "name" or "essence" of how an object appears to the external world, so I'd say it's more than just presentational. 有人可能认为toString()做同样的事情,但toString()也可以被认为是一个对象如何出现在外部世界的“名称”或“本质”,所以我要说它不仅仅是表现。

Hope this helps, and kudos for thinking about Clean Code, Decoupling, and Maintainability from the beginning. 希望这有助于从一开始就考虑清洁代码,解耦和可维护性。

For an example of principle #2 in action--using the Strategy Pattern , adhering to the Single Responsibility Principle , the Open/Closed Principle and allowing for Inversion of Control via Dependency Injection -- compare the following approach (graciously provided by @SteveJ): 对于行动中的原则#2的一个例子 - 使用策略模式 ,遵守单一责任原则开放/封闭原则并允许通过依赖注入进行控制反转 - 比较以下方法(由@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
        }
    }

}

With this one (in "mostly correct" Groovy): 有了这个(在“大多数正确的”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}"

As @SteveJ has observed: 正如@SteveJ所观察到的:

" So the you have different formatting "strategies" and you can switch between them...I had this idea that you would set the formatting once and be stuck with it...AND if you want to add another formatting style, you don't have to open up and rewrite the address class, but write a new separate style and inject it when you want to use it." “所以你有不同的格式”策略“,你可以在它们之间切换......我有这个想法,你会设置格式一次,并坚持下去...如果你想添加另一种格式风格,你不要不必打开并重写地址类,但要编写一个新的单独样式,并在想要使用时注入它。“

.NET SOLUTION: .NET解决方案:

Overriding Object.ToString() seems to be the most logical solution. 重写Object.ToString()似乎是最合理的解决方案。 This makes it clean to use in situations such as: Console.WriteLine("Home Address: {0}", homeAddress); 这样可以在以下情况下使用它: Console.WriteLine("Home Address: {0}", homeAddress);

If you wish to provide additional formatting , the Address class should implement IFormattable . 如果您希望提供其他格式 ,Address类应实现IFormattable

Also, you should create an AddressFormatter class that implements from IFormatProvider and ICustomFormatter . 此外,您应该创建一个从IFormatProviderICustomFormatter实现的AddressFormatter类。

The MSDN links provide very well put examples (a BinaryFormatter and a AcctNumberFormat), but if those aren't enough also look at this good example: PhoneFormatter MSDN链接提供了非常好的放置示例(BinaryFormatter和AcctNumberFormat),但如果这些还不够,还可以看一下这个好例子: PhoneFormatter


Additionally, if you do decide to go full out on this and implement IFormattable and a custom IFormatProvider/ICustomFormatter then I'd suggest having your ToString() simply call to your ToString(String format, IFormatProvider formatProvider) with a default provider. 另外,如果您决定全面了解并实现IFormattable和自定义IFormatProvider / ICustomFormatter,那么我建议让ToString()简单地使用默认提供程序调用ToString(String格式,IFormatProvider formatProvider)。 That way you can account for things like localization and types of addresses (short, long, etc). 这样你就可以解决本地化和地址类型(短,长等)的问题。

Using toString requires no additional baggage outside the function itself; 使用toString在函数本身之外不需要额外的行李; seems like the simplest solution. 似乎是最简单的解决方案。 It's there for a reason, right? 这是有原因的,对吗?

Usually i divide presentation layer from data layer. 通常我从数据层划分表示层。 Presenting it to a GUI seems to me something related to the presentation layer, not the data layer. 将它呈现给GUI在我看来是与表示层相关的东西,而不是数据层。

I would suggest you to put a function somewhere in your presentation layer that will convert the address to string. 我建议你在表示层的某处放置一个函数,将地址转换为字符串。

Presentation of data is not related to data! 数据的呈现与数据无关!

A static method is good. 静态方法很好。 A converter class would be better, you can keep one single instance for your application but you can replace it or write another if you are moving your application from GUI to WEB with another format, or if in one window you want to show everything and in another window you want to show only part of the informations or informations formatted in another way. 转换器类会更好,你可以为你的应用程序保留一个单独的实例,但如果你将应用程序从GUI移动到另一种格式的WEB,或者你想在一个窗口中显示所有内容,你可以替换它或写另一个实例另一个窗口,您只想显示以其他方式格式化的部分信息或信息。

There are several model you can follow, for example Microsoft WPF uses totally another approach, the MVVM, Model View View Model, that will allow you to divide very well data layer from business logic from presentation layer. 您可以遵循几种模型,例如Microsoft WPF使用完全另一种方法,即MVVM,模型视图视图模型,这将允许您从表示层划分业务逻辑中的数据层。

I usually override ToString in C# or toString in java only for debugging purposes (presenting a string that i can use for debug) or for some kind of simple serialization to string, usually putting also a FromString (or fromString method in java). 我通常在C#中覆盖ToString,在java中覆盖toString仅用于调试目的(提供可用于调试的字符串)或用于某种简单序列化到字符串,通常还在java中添加FromString(或fromString方法)。 One example is custom types like Point, Vector, Matrix and so on. 一个例子是自定义类型,如Point,Vector,Matrix等。

Talking about C# world.. 谈论C#世界..

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

Then in your form (for example). 然后在你的表格(例如)。

AddressToStringConverter myConverter = new AddressToStringConverter();

public Address CurrentSelectedAddress { get { ... } }

public button1_click(object sender, EventArgs e)
{
    button1.Text = myConverter.Convert(address);
}

If you want you can imlpement other useful interfaces like for example ITypeConverter 如果您愿意,可以使用其他有用的界面,例如ITypeConverter

toString() is the most flexible and convenient, being implicitly called when you combine an object of the Address class with a String, as in System.out.println("My address is " + objectOfAddressClass). toString()是最灵活和方便的,在将Address类的对象与String组合时会隐式调用,如System.out.println(“我的地址是”+ objectOfAddressClass)。

The only reason I can think of to not override toString() is if you need to alter the formatting. 我可以想到不覆盖toString()的唯一原因是你需要改变格式。 Then you would need different methods (as in toMailingString() and toShortFormString() and so on) or a parameterized method (as in toMailingString(boolean useShortForm) or whatever), but either way, toString() won't cut it. 那么你需要不同的方法(如toMailingString()和toShortFormString()等)或参数化方法(如toMailingString(boolean useShortForm)或其他),但无论如何,toString()都不会删除它。

Of course, you can (and should) do both. 当然,你可以(而且应该)做到这两点。 Have toString() as your default, probably calling one of your specific format methods, and then have your other helper methods for alternate formats. 将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
        }
    }

}

I think that a toString() method that returns a string is your best approach. 我认为返回字符串的toString()方法是最好的方法。 If you have an Address instance, let's say address , then it is obvious what address.toString() does. 如果你有一个地址实例,让我们说address ,那么很明显是address.toString()作用。 The fact that toString() isn't directly associated with Address doesn't really change anything. toString()Address没有直接关联的事实并没有真正改变任何东西。

You've tagged your post with Java, so I'll answer for Java (and Swing, more specifically). 你已经用Java标记了你的帖子,所以我将回答Java(更具体地说是Swing)。 This task is normally the task of a specific TableCellRenderer. 此任务通常是特定TableCellRenderer的任务。 If the same format must be used for other visual components, I would indeed extract the formatting inside an instantiatable class (solution 2). 如果必须为其他可视组件使用相同的格式,我确实会在可实例化的类中提取格式(解决方案2)。 This would allow subclasses to customize the format if needed. 这将允许子类在需要时自定义格式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM