OO 设计建议 - toString

发布于 2024-12-11 23:11:51 字数 823 浏览 0 评论 0原文

所以我得到了 Address 类:

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

我想获得它的可读版本,比如说,显示在网格列中。

实现此目的的最佳且简洁的方法是什么?

  1. 中的 toString 方法(我个人不喜欢这种方法,因为“toString”与地址不直接相关
  2. Address类 代码>ReadableAddressFormatter
    • ReadableAddressFormatter(地址 addressToFormat)
    • public String getFormatted()
  3. 上一类,但 getFormmated< /code> 将是静态的,接收 Address 实例并返回字符串
  4. Other?请提出建议。

我正在寻找一个好的设计,同时关注干净的代码解耦可维护性

So I got the Address class:

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)
  2. class ReadableAddressFormatter
    • ReadableAddressFormatter(Address addressToFormat)
    • public String getFormatted()
  3. Previous class but getFormmated would be static, receiving the Address instance and returning the string
  4. Other? Suggestions please.

I'm looking for a good design, focusing also in Clean Code, Decoupling and Maintainability.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

尝蛊 2024-12-18 23:11:51

所有这些方法都已被使用,并且没有办法提供“上下文无关”的最佳实践。软件工程中的最佳答案通常是“视情况而定”。也就是说,让我们逐一分析:

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

希望这对您有所帮助,并感谢您从一开始就考虑了整洁代码、解耦和可维护性。

有关原则 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
        }
    }

}

与此方法(在“大部分正确”中) 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 所观察到的:

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

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. 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). 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.

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):

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):

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:

" 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."

毅然前行 2024-12-18 23:11:51

.NET 解决方案:

重写Object.ToString() 似乎是最合乎逻辑的解决方案。这使得在以下情况下使用起来更加简洁: Console.WriteLine("Home Address: {0}", homeAddress);

如果您希望提供额外的格式,则地址类应实现 IFormattable

另外,您应该创建一个从 IFormatProviderICustomFormatter

MSDN 链接提供了很好的示例(BinaryFormatter 和 AcctNumberFormat),但如果这些还不够,还可以看看这个很好的示例:PhoneFormatter


此外,如果您决定全力以赴并实现 IFormattable 和自定义IFormatProvider/ICustomFormatter 那么我建议您的 ToString() 只需使用默认提供程序调用 ToString(String format, IFormatProvider formatProvider) 即可。这样您就可以考虑本地化和地址类型(短、长等)等问题。

.NET SOLUTION:

Overriding Object.ToString() seems to be the most logical solution. This makes it clean to use in situations such as: Console.WriteLine("Home Address: {0}", homeAddress);

If you wish to provide additional formatting, the Address class should implement IFormattable.

Also, you should create an AddressFormatter class that implements from IFormatProvider and ICustomFormatter.

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


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. That way you can account for things like localization and types of addresses (short, long, etc).

番薯 2024-12-18 23:11:51

使用 toString 不需要函数本身之外的额外负担;似乎是最简单的解决方案。它的存在是有原因的,对吧?

Using toString requires no additional baggage outside the function itself; seems like the simplest solution. It's there for a reason, right?

海风掠过北极光 2024-12-18 23:11:51

通常我将表示层与数据层分开。在我看来,将其呈现给 GUI 似乎与表示层相关,而不是数据层。

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

数据的呈现与数据无关!

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

您可以遵循多种模型,例如 Microsoft WPF 使用完全另一种方法,即 MVVM(模型视图视图模型),它允许您很好地将数据层、业务逻辑和表示层分开。

我通常会重写 C# 中的 ToString 或 java 中的 toString,仅用于调试目的(呈现可用于调试的字符串)或用于某种简单的字符串序列化,通常还会放置 FromString(或 java 中的 fromString 方法)。一个例子是自定义类型,如点、向量、矩阵等。

谈论 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

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.

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.

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.

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). One example is custom types like Point, Vector, Matrix and so on.

Talking about C# world..

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

☆獨立☆ 2024-12-18 23:11:51

toString() 是最灵活和方便的,当您将 Address 类的对象与 String 组合时,会隐式调用,如 System.out.println("My address is " + 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
        }
    }

}

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).

The only reason I can think of to not override toString() is if you need to alter the formatting. 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.

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.

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
        }
    }

}
地狱即天堂 2024-12-18 23:11:51

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

You've tagged your post with Java, so I'll answer for Java (and Swing, more specifically). This task is normally the task of a specific TableCellRenderer. If the same format must be used for other visual components, I would indeed extract the formatting inside an instantiatable class (solution 2). This would allow subclasses to customize the format if needed.

神魇的王 2024-12-18 23:11:51

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

I think that a toString() method that returns a string is your best approach. If you have an Address instance, let's say address, then it is obvious what address.toString() does. The fact that toString() isn't directly associated with Address doesn't really change anything.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文