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?
toString
method inside class Address
(I personally don't like this approach, as 'toString' is not directly related to an Address)ReadableAddressFormatter
ReadableAddressFormatter
(Address
addressToFormat
)String getFormatted()
getFormmated
would be static, receiving the Address
instance and returning the stringI'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:
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."
.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).
Using toString
requires no additional baggage outside the function itself; seems like the simplest solution. It's there for a reason, right?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With