OO设计建议 - toString

时间:2011-10-26 18:00:38

标签: c# java oop coding-style

所以我得到了Address课程:

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

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

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

    toString内的
  1. Address方法(我个人不喜欢这种方法,因为'toString'与地址没有直接关系
  2. 班级ReadableAddressFormatter
    • ReadableAddressFormatterAddress addressToFormat
    • public String getFormatted()
  3. 以前的类但getFormmated是静态的,接收Address实例并返回字符串
  4. 其他?建议请。
  5. 我正在寻找一个好的设计,专注于清洁代码去耦可维护性

7 个答案:

答案 0 :(得分:7)

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

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

    有关行动原则#2的示例 - 使用Strategy Pattern,遵守Single Responsibility PrincipleOpen/Closed Principle并允许Inversion of Control via 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所观察到的那样:

      

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

答案 1 :(得分:6)

.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) 。这样你就可以解决本地化和地址类型(短,长等)等问题。

答案 2 :(得分:2)

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

答案 3 :(得分:2)

通常我从数据层划分表示层。将它呈现给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

答案 4 :(得分:1)

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

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

当然,你可以(并且应该)做到这两点。将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
        }
    }

}

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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