ОО Дизайн Консультация - toString - PullRequest
8 голосов
/ 26 октября 2011

Итак, я получил класс Address:

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

И я хочу получить его читаемую версию, скажем, для отображения в столбце сетки.

Какой лучший и краткий способ реализовать это?

  1. toString метод внутри класса Address ( Мне лично этот подход не нравится, так как toString не имеет прямого отношения к адресу )
  2. класс ReadableAddressFormatter
    • ReadableAddressFormatter (Address addressToFormat)
    • публично String getFormatted()
  3. Предыдущий класс, но getFormmated будет статическим, получая экземпляр Address и возвращая строку
  4. Другое? Предложения, пожалуйста.

Я ищу хороший дизайн, фокусируясь также на Чистый код , Отделение и Ремонтопригодность .

Ответы [ 7 ]

7 голосов
/ 26 октября 2011

Все эти методы были использованы, и нет способа предложить "независимую от контекста" лучшую практику. Лучший ответ в программной инженерии, как правило, «это зависит». Тем не менее, давайте проанализируем каждый:

  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:

"Так что у вас есть разные" стратегии "форматирования, и вы можете переключаться между ними ... У меня была идея, что вы однажды установите форматирование и застрял с этим ... И если вы хотите добавить другое форматирование стиль, вам не нужно открывать и переписывать класс адресов, но напишите новый отдельный стиль и добавьте его, когда захотите его использовать. "

6 голосов
/ 26 октября 2011

.NET РЕШЕНИЕ:

Переопределение Object.ToString() представляется наиболее логичным решением.Это упрощает использование в таких ситуациях, как: Console.WriteLine("Home Address: {0}", homeAddress);

Если вы хотите обеспечить дополнительное форматирование , класс Address должен реализовывать IFormattable.

Кроме того, вы должны создать класс AddressFormatter, который реализует из IFormatProvider и ICustomFormatter.

Ссылки MSDN предоставляют очень хорошие примеры(BinaryFormatter и AcctNumberFormat), но если этого недостаточно, посмотрите также на этот хороший пример: PhoneFormatter


Кроме того, если вы решите полностью использовать этои реализовать IFormattable и пользовательский IFormatProvider / ICustomFormatter, тогда я бы предложил, чтобы ваш ToString () просто вызывал ваш ToString (формат String, IFormatProvider formatProvider) с поставщиком по умолчанию.Таким образом, вы можете учитывать такие вещи, как локализация и типы адресов (короткие, длинные и т. Д.).

2 голосов
/ 26 октября 2011

Обычно я делю уровень представления данных на уровень данных.Представление его в GUI кажется мне чем-то связанным со слоем представления, а не со слоем данных.

Я бы предложил вам поместить где-нибудь на вашем уровне представления функцию, которая преобразует адрес в строку.

Представление данных не связано с данными!

Статический метод хорош.Класс конвертера был бы лучше: вы можете оставить один экземпляр для своего приложения, но вы можете заменить его или написать другой, если вы перемещаете свое приложение из GUI в WEB с другим форматом или если в одном окне вы хотите показать все и вВ другом окне вы хотите отобразить только часть информации или информации, отформатированной другим способом.

Существует несколько моделей, которым вы можете следовать, например, Microsoft WPF использует совершенно другой подход, MVVM, Model View View Model, которыйпозволит вам очень хорошо отделить уровень данных от бизнес-логики от уровня представления.

Я обычно переопределяю ToString в C # или toString в Java только для целей отладки (представляя строку, которую я могу использовать для отладки) или для некоторыхвид простой сериализации в строку, обычно помещая также FromString (или метод fromString в Java).Одним из примеров являются пользовательские типы, такие как 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

2 голосов
/ 26 октября 2011

Использование toString не требует дополнительного багажа вне самой функции;кажется самым простым решением.Это по какой-то причине, верно?

1 голос
/ 26 октября 2011

toString () является наиболее гибким и удобным, вызывается неявно, когда вы объединяете объект класса Address со строкой, как в 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
        }
    }

}
0 голосов
/ 26 октября 2011

Вы пометили свой пост Java, поэтому я отвечу за Java (и Swing, более конкретно). Эта задача обычно является задачей конкретного TableCellRenderer. Если тот же формат должен использоваться для других визуальных компонентов, я действительно извлеку форматирование внутри инстанцируемого класса (решение 2). Это позволит подклассам настраивать формат при необходимости.

0 голосов
/ 26 октября 2011

Я думаю, что toString() метод, который возвращает строку, является вашим лучшим подходом. Если у вас есть экземпляр Address, скажем, address, то очевидно, что делает address.toString(). Тот факт, что toString() напрямую не связан с Address, на самом деле ничего не меняет.

...