Join 306,961 Programmers for FREE! Get instant access to thousands of experts, tutorials, code snippets, and more! There are 1,895 people online right now. Registration is fast and FREE... Join Now!
There is something I am often faced to when designing and I would like to have some feedbacks.
If for example I have an object Country and an object City, and I say that a country is composed of cities. I can think of 3 possible design.
design 1
class Country { private String countryName; private List cities; //list with all cities of the country }
class City { private String cityName; }
design 2
class Country { private String countryName; }
class City { private String cityName; private Country country; // country in wich the city is located }
design 3
class Country { private String countryName; private List cities; //list with all cities of the country }
class City { private String cityName; private Country country; // country in wich the city is located }
What will you say? What are the pros and cos of each ones?
For example in design3 , can't we say that the DRY (don't repeat yourself) concept is broken when we want to know in wich country is located a city? We can have this information by the variable country from the class City but also by looping every cities of the application and then looping the variable cities to find the city we are looking for.
This post has been edited by anirelles: 3 Nov, 2009 - 09:57 AM
The design is going to depend on the business case. If your application only needs to have access to which cities are in a specific country, then scenario one is correct and sufficient.
If your application needs to be able to identify the country a specific city is in, and has no need to know all the cities within a country, scenario two is sufficient.
If your application needs to be able to do both, find all the cities in a country and find a country that a specific city is in, scenario three would be an acceptable choice, but could also be handled in other ways.
The DRY concept isn't necessarily broken by using scenario three, because it is not shown that a collection of cities exists and is available to iterate.
If your application needs to be able to do both, find all the cities in a country and find a country that a specific city is in, scenario three would be an acceptable choice, but could also be handled in other ways.
The DRY concept isn't necessarily broken by using scenario three, because it is not shown that a collection of cities exists and is available to iterate.
Thanks. Let's say we are in this scenario. What are the others ways you are thinking about? I am interested in. Instead of having a list of Cities in Country we could have a list of cities' ID but to me it's not a good OO design. Maybe I'm wrong.
The thing is that if I modify the name of a city I also have to modify the name of the same city in the Country class.
Maybe we can say that DRY principle is violated for performance reason in the scenario where we want to know in which country is located a city.
I would actually go with a cross between 1 and 3 for this personally. Something along the lines of this:
CODE
class Country { private String countryName; private List<City> cities; // C# syntax for a list of cities public addCity(City city); public removeCity(City city); }
class City { private String cityName; private String countryName; public String getName(); public void setName(string cityName); public String getCountry(); public void setCountry(String countryName); }
I would go with that. The reason I would is a Country should be able to work with the Cities that belong to it. A City should know what Country it belongs to but should never be allowed to do anything with that Country. It makes sense to me to have the name of the country as an attribute of City. This way you could say to a Country that City X has been renamed to City Y. The City would get a call from the Country class saying: "Hey, this is your name now." You could remove cities from a Country. The removing scenario could happen in a war. You could also easily add a new City to a Country.
I'm really liking this discussion. I would really like to read other people's ideas on this.
A City should know what Country it belongs to but should never be allowed to do anything with that Country. It makes sense to me to have the name of the country as an attribute of City.
Imagine that later we want to know not only the name of the country but also the name of the president and the currency of the country, with your design we would have to add these attributes to the City class. Isn't it better to have a reference to a Country so we don't have to modify the code of City when specifications change?
Imagine that later we want to know not only the name of the country but also the name of the president and the currency of the country, with your design we would have to add these attributes to the City class. Isn't it better to have a reference to a Country so we don't have to modify the code of City when specifications change?
That is an excellent point. (I told you I'm enjoying this thread.) It would be a good idea to think of future versions of the program if the scope of the program were to change eventually down the road.
However, think of how your program is going to work. Would the main class the program works with be City or Country? In my opinion I would work in terms of Country. One reason is in this world there are several cities with the same name in different countries. There is, for example, London Canada and London England. If you were to say: What is the currency of London? Which London are you looking for? The way I would work it in the program is that you would work with countries. If the user wanted to know the currency of a city they would supply the country name and the city name. The program would first look for the country. It would then check the list of cities to see if there is a city of that name in the country. In this design a City would not be able to change anything in a Country
Your models two and three would be a case of dependency. The City class would depend on the Country class. It would, for example, have to get its currency, president, etc from the Country class. There would be coupling involved in this program. Some times you have to deal with coupling. Just try and minimize it. You would want to limit the public methods of the Country class though so that the City class could not change things in the Country class. For example, if you had a public method in the Country class called setLeader, which is reasonable because leaders change, a City would be able to change the leader of the whole country! That is not something you would want. You could implement a Clone interface and pass copies of the Country to the City and have both a List of City and a List of Country. This would also increase the memory used by the program.
I think a design flaw in 3 (and especially SixOfEleven's version of it) is that you could imagine a situation like the following:
CODE
Country scot = new Country("Scoltand"); City city = new City("Glasgow"); scot .add("Glasgow"); city.setCountry("England");
This is clearly wrong but the sata type allows it. Designs one and two might be slower for certain operations (i.e. needing a search) but you can guarantee you won't have an illogical state. Design three will be more efficient in run time but the developer has more opportunity to introduce bugs.
One option is to go with design three but reduce the visibility of the setCountry method in the City class. Depending on the language, make it package private or available only to friendly classes. That way, adding a city to a country will call setCountry automatically. Even better if setCountry throws an exception if it had been set previously.
This brings up another issue... the public interface to the whole thing. Now, what I'm about to suggest may be totally inappropriate to your project but you could have a third class World which has methods such as:
getCountry(String name) getCity(String name) getCitlesInCountry(Country c) getCountryOfCity(City c) ... and so on
This way you can have a very well-defined public interface for your model and can have rigorous test code. Whatever internal representation you have for your countries and cities, you can change whenever you want as long as all the tests you write still pass.
Having a World class with a set of well defined public methods would be a good approach to this problem. Probably better than any of the other approaches discussed so far This is why it is good to work in teams with other programmers when you are working on analysis and design. Each team member brings a different perspective to problem.
About this code:
CODE
Country scot = new Country("Scoltand"); City city = new City("Glasgow"); scot .add("Glasgow"); city.setCountry("England");
Yes, that is definitely a bad thing. Much like the city being able to set the leader of a country. It is unlikely that you would ever need to set the country after creating the city. I was thinking of strategy games where the player captures an enemy city. In the real world it his highly unlikely that a single city would change countries. Like cfoley said the setCountry method could be access limited using packages/friends to the City and Country class. Meaning that only the Country or City class have access to that method and any other methods that would create a no-no.
There is, however, the case where countries separate or merge. Here in Canada there is a separatist movement where one province wants to separate from the rest of the country. If that was to happen how would you solve that problem? Also there is always the possibility that Northern Ireland and Southern Ireland, or North Korea and South Korea would merge.
With the World model with a well defined public interface those situations could easily be remedied. The best idea would be to create two new instances of Country for separation and divide the cities to the new countries. For the merging, create a new Country and add the cities from the old countries.
In any event you would definitely want to do a lot of validation and error checking with this type of program. The more I think about it the more I like the World class idea with a strongly defined public interface.
In the real world it his highly unlikely that a single city would change countries.
What about Hong Kong ?
QUOTE
For example, if you had a public method in the Country class called setLeader, which is reasonable because leaders change, a City would be able to change the leader of the whole country! That is not something you would want. You could implement a Clone interface and pass copies of the Country to the City
Good point. It is necessary to find a way to avoid City to have access of SetMethods of Country.
QUOTE
In any event you would definitely want to do a lot of validation and error checking with this type of program. The more I think about it the more I like the World class idea with a strongly defined public interface.
Brilliant. I can keep design 3 and rely on World class to manipulate Country and City correctly.
But I have to be very careful with the visibility of setters method to avoid that :
QUOTE
City city = World.getCity("Mexico"); city.setCountry("England");
This post has been edited by anirelles: 5 Nov, 2009 - 04:44 AM
So summing up. For my initial problem I can decide to use design3 admitting that I break DRY principle but for performance reason. And to minimize the risk of illegal state I introduce a new class (World) which is the unique place for creation and update of my objects. Is there some design pattern which looks like that? Maybe we have figured out a new design pattern
This post has been edited by anirelles: 5 Nov, 2009 - 04:49 AM
I tried to code design 3 and World class and I would like to give feedbacks:
Here there is the code of World :
CODE
public class World {
public static Country getCountry(String name){ //read the country from database return new CountryImpl(name); }
public static Country addCityToCountry(Country oldCountry, String newCityName){
//new country is just the old country where we will add a new city CountryImpl newCountry = new CountryImpl(oldCountry.getName());
//we create the city we want to add to the new country CityImpl newCity = new CityImpl(newCityName); newCity.setCountry(newCountry);
//all cities of the old country must be part of the new country //and we make them point to the new country for (Object oldCountryCity : oldCountry.getCities()) { CityImpl city = (CityImpl)oldCountryCity; city.setCountry(newCountry); newCountry.addCity(city); }
//finally we add the new city to the new country newCountry.addCity(newCity); return newCountry;
public CountryImpl(String name){ cities = new ArrayList(); this.name = name; }
public String getName() { return name; }
void addCity(City city){ cities.add(city); }
}
My main program is :
CODE
public static void main(String[] args) { Country country = World.getCountry("France"); country = World.addCityToCountry(country, "Nice"); country = World.addCityToCountry(country, "Marseille"); World.saveCountry(country); }
I'm not happy with World class, it's a lot of code for not so much. Finally I changed my mind and the simple solution is like SixOfEleven said :
QUOTE
However, think of how your program is going to work. Would the main class the program works with be City or Country? In my opinion I would work in terms of Country.
I'll access the cities through Country so I just have to keep a reference to the Country in my main program So it's design 1 I'll choose.
This post has been edited by anirelles: 5 Nov, 2009 - 07:16 AM