18

I am currently working on problem with a chicken or egg first situation. Basically, I am designing a solution which goes like this:

  • World is a collection of countries;
  • Each Country has a name, flag and president (all are strings);
  • There will be relations between countries, like country A is friend or supporter of country B. So there will be many countries which are either friends of other countries or the leader countries;
  • There will be some conditions for whether a country is friend or not, based on the number of trades between the countries. If country X exports more to say countries P,Q,R,S then X is the leader country and P,Q,R,S are its friends, and so on for other countries.

At any point of time I may need to handle the request like: who is the ultimate leader country (which has max supporters or friends etc), or who are supporters of the ultimate leader country, or who are supporters of any given leader country, etc. The trick here is the basic properties of friends or leaders are same (like every Country has name, flag, president)

I want a good skeleton structure to address the basic service requirements of this problem statement. But I need a design able to handle any future extensions, such as successors, descendants, duration of ruling, family of presidents, etc.

I am confused in deciding which approach to follow: do I need to make (Approach 1) or should I define (Approach 2).

Approach 1: Country is part of World

class World
{
    class country *country_list // not taking array , so that i need not to limit the list
};

Approach 2: Country defined first and World inherits from it

This looks odd, because Country is a small entity and World is big entity. And, then what would be the contents of world, but again its a list of countries.

class World : Inherit class country
{
    // not sure what should be the content here 
    // can getcounty();
    // add_country_to_world();
    //
};

//not sure wether to make this abstract or normal.
//no duplicates countries are allowed.

class country
{
    string countryname;
    string flag;
    string president;
};

Then I would like to make a mapper which contains a key (made of country name) and values are its details, like country is friend or leader, no of friends, so that i can directly check who is ultimate leader.

19
  • 34
    I'm against designing classes when all you know is a name and the data. OOP makes it easy to add new classes but hard to add new methods. Can we please think about the methods before settling on a structure? Tell me how this thing will be used. Commented Nov 5, 2019 at 18:05
  • 37
    It's sort of bizarre to propose that Country inherit from World. Which makes me very curious what you were thinking. If you are looking for some kind of recursive structure, perhaps Country and World could both inherit from a common class or implement a base interface, g.e. ILandMass or something. That would make a lot more sense.
    – John Wu
    Commented Nov 5, 2019 at 19:00
  • 24
    Without trying to offend, your question shows a deep misunderstanding of inheritance, to a point where I would suggest you brush up on it using tutorials. It's ineffective to provide feedback on an idea that is (again, no offense) half-baked and not based on a faulty understanding of OOP core principles.
    – Flater
    Commented Nov 5, 2019 at 20:21
  • 17
    Why did you consider inheritance?
    – minseong
    Commented Nov 5, 2019 at 20:27
  • 14
    Over decades, developers collectively began to understand that inheritance is a sharp tool and often the wrong one. (Google "inheritance breaks encapsulation" for one aspect of the subject.) Beginner classes and books still introduce inheritance as one of the very first topics related to OOP, which IMO is a huge pedagogical mistake. A student working on a problem like the one described shouldn't even know about inheritance yet. Commented Nov 5, 2019 at 23:41

11 Answers 11

126

I wouldn't use any form of inheritance for World and Country. The World is not a Country, and a Country is not the World.

Instead, the World is a "bag" which contains many Countries. By "bag" I mean some kind of container class of your choosing (linked list, set, dictionary/map, or whatever). Pick whichever container type allows you to most efficiently find Countries.

11
  • 73
    In other words. Use Composition|Aggregation instead of inheritance.
    – Laiv
    Commented Nov 5, 2019 at 15:33
  • 6
    @laiv actually Composition|Aggregation would only be appropriate if World had an interface of messages to delegate to the countries. That hasn't been shown yet. I could imagine one: earth.bomb() could be delegated to each country. But if all we're doing is writing queries against this object graph a simple collection might be all a world needs to be. Commented Nov 5, 2019 at 16:33
  • 1
    @candied_orange I could be wrong but I have assumed that there's no behavior. When it comes to data structures, inheritance does more harm than good. The comment add_country_to_world() is also asking for aggregation or composition. May be the OP should clarify if these classes are mere POJOS or they are meant to be something else. In any case, Simon hits on the nail. This case is a problem of unapropiated abstraction.
    – Laiv
    Commented Nov 5, 2019 at 19:09
  • 4
    @Laiv yes he does. But he never advocated composition or aggregation for a darn good reason: we have no idea how to abstract World yet. Without any methods or use cases we're clueless. So for now I'm perfectly happy letting earth be nothing more than a named collection of countries just like Simon said. Commented Nov 5, 2019 at 20:07
  • 1
    @candied_orange: "bag" wasn't used here as "bag of functions" but rather as "any form of collection". Collection/list/array/set/.... pick your preferred term. The distinctions between these names matter in code but not so much in everyday English, hence the more abstract "bag" to avoid pointing towards a particular structure.
    – Flater
    Commented Nov 6, 2019 at 12:58
19

Here is a thought or two:

  1. Are you sure you even need to model the World? Based on your description it doesn't seem to have any effect. Yes it encapsulates your Countries, but if thats all the program does, then the programs encapsulation should be fine.
  2. You need to model relations somehow. Exactly how, depends on your demands, but I would think something like the code below.
  3. Consider some way of managing the different types of leaderships. Countries in the world are lead in wildly different ways. Some are democracies with a prime minister or president while others are dictatorships. If you need to use the leadership for something, this needs to be adressed.

Relations class (simplified pseudo code)

class CountryRelation
{
    Country FirstCountry { get; set; }
    Country SecondCountry { get; set; }
    RelationshipType Relationship { get; set; }
}

enum RelationshipType {
    Friends,
    Enemies,
    Neutral
};
8
  • 2
    You could make it so that a Country owns a set of CountryRelations, so you don't a FirstCountry/SecondCountry property - you just need one for the TargetCountry. (EDIT - though if it were a database table, then yes that makes sense) Commented Nov 5, 2019 at 9:25
  • 2
    @jk.: It depends on your definition. Lots of countries make a difference between Head Of State and Head Of Government, for example. In Germany, the President is the Head Of State but has no powers except a loophole in the Constitution. (The Constitution says that the President signs bills into law, and scholars have constructed from this that the President could refuse to sign a bill that he deems unconstitutional.) The Head Of Government is the Chancellor. In Canada, the Queen Of England is the Head Of State, who is also the Head Of State of Australia, for example. Commented Nov 5, 2019 at 9:26
  • 4
    @JörgWMittag The canadian head of state is the Queen of Canada. That role is held by Elizbeth II, who is also Queen of The United Kingdom of GB & NI. There is no 'Queen of England' role.
    – bdsl
    Commented Nov 5, 2019 at 12:18
  • 2
    If you really want a fun one, look up Andorra and Switzerland, who have interesting heads of state. And there's an argument that the head of state of Canada is actually the Governor General, who represents the Queen of Canada. (And yes, the last Queen of England was Anne.)
    – TRiG
    Commented Nov 6, 2019 at 12:40
  • 1
    I think it might be an okay idea to have RelationshipType be some class that uses a float from -1 to 1 and helper methods to get the current status. It would allow more flexibility, if required.
    – GammaGames
    Commented Nov 6, 2019 at 15:37
12

I think your first requirement is the most telling:

World is a collection of countries

The remaining requirements go on to talk about countries, leaders and alliances. There is one thing I'm sure about: Without any behavior, World is not a class. At best it is a generic collection:

var world = new Collection<Country>();

Given how detailed the requirements are for countries and alliances, a collection of Country objects might not even be necessary. It seems to me the main entities in this model are Country (as you've stated) and Alliance — a relationship between two or more countries.

var world = new Collection<Country>();

world.Add(new Country("United States of America"));
world.Add(new Country("Portugal"));
world.Add(new Country("Saudi Arabia"));

var natoAllianceTypes = new AllianceType[] { AllianceType.Military, AllianceType.Political };
var nato = new Alliance("North Atlantic Treaty Organization", natoAllianceTypes);

nato.AddMemberNation(world.Single(c => c.Name == "United States of America"));
nato.AddMemberNation(world.Single(c => c.Name == "Portugal"));

var opecAllianceTypes = new AllianceType[] { AllianceType.Economic };
var opec = new Alliance("Organization of the Petroleum Exporting Countries", opecAllianceTypes);

opec.AddMemberNation(world.Single(c => c.Name == "Saudi Arabia"));

// so on, and so forth...

The key here is the Alliance class that glues the countries together. This allows you to model the relationships you desire. Attributes specific to a country go on the Country class. Attributes specific to a relationship between two or more countries belong on the Alliance class. Determining who the "lead" country is sounds like a concern for the Alliance class as well, or maybe a strategy object if you need to abstract this calculation. For instance, NATO is both a political and military alliance. The "political" leader might not necessarily be the "military" leader.

The world? Well, there isn't much to say about this object. It is the canonical source of what countries exist (but not necessarily "officially recognized" by the international community, which might be yet another alliance: United Nations, anyone?).

4
  • Alliance could be a good idea, assuming it's always mutual. Each Country could store an Alliance instance; alternatively, World could have a vector of Alliance instances. It depends mainly on what kind of lookups are desired.
    – Andrew
    Commented Nov 5, 2019 at 17:53
  • 1
    @Andrew: This doesn't even require "mutual" relationships. Saudi Arabia is a member of OPEC, and the United Nations. The USA and Portugal are also members of the UN, but not OPEC. It is hard to believe a relationship can only be one-sided. That is not a relationship (both metaphysically and literally). The relationship can be lopsided, that's for sure. The USA imports a lot of oil from OPEC countries. The USA is not a member of OPEC, but it certainly has an different economic alliance with Saudi Arabia allowing it to import oil. Commented Nov 5, 2019 at 17:59
  • Making world a collection of countries is a bad design. Instead make a (static) class world which has a collection of countries. See stackoverflow.com/a/21694054/2190035
    – user11909
    Commented Nov 8, 2019 at 14:39
  • @user2190035: Without behavior, just a simple collection will suffice. And be careful with static classes that contain static state. They can become glorified global variables very quickly. Commented Nov 8, 2019 at 17:13
8

Is the world a country ?

Your most basic entity is the Country. The World is the aggregation of all the countries (193 member states according to the United Nations). Clearly, World has no president and no flag. It is therefore not a Country and shall not inherit from it.

Isn't something missing from this model ?

You've nevertheless missed something important for your problem: countries can be aggregated to form a larger Group depending on their relations. And all your questions basically are about the properties of these groups.

enter image description here

Whether World would be a Group (or an instance thereof ) is a philosophical question, and I'll leave it up to you to decide for your model.

More on country aggregations

The Group provides for more than a Country: it has member countries, it may have a leader country (but not necessarily), it may be represented by the head of the leading state or have its own political representation, it may have a formal or an informal capital, etc... We guess that there may be different kind of Group such as Unions, Federations, Blocs, not just the de facto groups defined by some relations. Groups will ensure you the perfect flexibility for future extensions and more subtle rules on how the whole react based on its parts.

The core question for your design is whether you want to use Country and Groups interchangeably, despite their difference:

  • If not, you can choose composition over inheritance without the slightest hesitation. This would be my recommended solution (see above).
  • If yes, you could design Country and Group based on the same Power interface, using the composite pattern (see hereafter).

enter image description here

In very broad terms, all your problem is about sets, subsets according to some membership rules, and partitions. You can easily extend the group functionality to handle them all. And you could engineer a relationship manager that could revise the economic groups based on new or updated relationships.

P.S.: Not all the states around the planet have a president. A more suitable generic term would be head of state.

2
  • 4
    How did you make those class diagrams, they are fantastic!
    – hooknc
    Commented Nov 5, 2019 at 23:07
  • 4
    @hooknc Thank you! I hope the diagrams not just look nice but also help to better understand the alternatives ;-) I did it with yUML, an online tool here
    – Christophe
    Commented Nov 5, 2019 at 23:17
3

I don’t want to get too pedantic if you’re just using shorthand, but you write in what looks like C++:

class World
{
    class country *country_list // not taking array , so that i need not to limit the list
};

You definitely would want to use a data structure here, not a C-style pointer. If nothing else, that saves you the trouble of re-implementing the constructor, destructor and insert operations of std::vector in C++, ArrayList in Java or C#, std::Vec in Rust, etc. Your instinct was correct and this kind of dynamic, resizable array is what you normally want here, but you shouldn’t reinvent the wheel.

If there’s some particularly important operation that you do particularly often, but is very slow on an array, such as deleting countries, finding which countries are in two different lists, merging two lists with no duplicates, inserting countries in the middle of a long list, etc., you might consider a different data structure. Be especially wary of any algorithm that would take quadratic time or worse. (However, if there are at most a few hundred countries in your world, this matters less.) If keeping it sorted at all times lets you optimize, such as by doing binary instead of exhaustive searches, or deducing that if a country were in the list you would have seen it already, you can do that.

0

I see the 'World' class more as a container of countries, not a base for country, Inheriting it for country doesn't sound right.

For Country, considering that the country name and it's flag will hardly change, you can have it as a enum.

Enum Country{
    ABC("abc", "abc_flag")


    private final String name;
    private final String flag;

    public String getName() {
        return name;
    }

    public String getFlag() {
        return flag;
    }

}

Presidents of country can change, and since there is a 1:1 mapping, You can have a simple EnumMap for this.

EnumMap<Country, String> countryPresident.

Your relationship type and mapping can be an enum as @Noceo mentioned.

enum RelationshipType {
    Friends,
    Enemies,
    Neutral
};
1
  • 2
    Enum for a Country itself would not be a good idea. There's a significant chance that Country will need other properties and maybe even actions (functions) or other complexities later. Using a map and an enum for the keys of that map, storing a map in each Country class, would be a better approach for this. In general, every "object" in the real world should be a class in the code.
    – Andrew
    Commented Nov 5, 2019 at 17:50
0

Welcome to Software Engineering Stackexchange!

At any point of time I may need to handle the request like: who is the ultimate leader country (which has max supporters or friends etc), or who are supporters of the ultimate leader country, or who are supporters of any given leader country, etc. The trick here is the basic properties of friends or leaders are same (like every Country has name, flag, president)

It seems to me that the relationship between Countries is based on an export information. This makes me think you could model it as a directed graph where each node is a coutry and each link from country A to B carries the information of how much country A exports to B. In term of implementation, you could implement it in several ways, like a class for each country with two maps, one for the export and the other for the import, where the key is the other country and the value is the amount.

Properties not related to the relation between countries but to a single country (flag etc) can be simple properties of this class.

I want a good skeleton structure to address the basic service requirements of this problem statement. But I need a design able to handle any future extensions, such as successors, descendants, duration of ruling, family of presidents, etc.

Having an extensible structure is good, but you should not do too much. These examples you make seem like they would be big enough to consider a refactoring if they ever become needed, so I would not consider them initially. Based on my previous idea, an example of a good future-proof choice would be to represent the relation of export-import with a class instead of a simple number, so that if the need to add information to the relation arise, you can do it simply.

0

It sounds like you're focusing too much on nomenclature.

Both World and Country are territories. In fact everything is a Territory. Territories contain territories so if I were you, I would reduce it down to a recursive collection of territories and determine the minimum number of properties needed to define territories for your use case.

For example, for a government you could have Person, Role, and Responsibility as base types for all relevant people. A person could hold many roles, and roles could have many responsibilities. With inheritance and hierarchies you could map out the complex networks of people that form governments and other entities.

For territories, a territory would contain many smaller territories, and each territory would contain a collection of Relationships and you could sub-type that with TradeRelationship and PoliticalRelationship etc.

Each person and territory would be a distinct entity which you would then connect together to form a network, and you would add properties and new sub-types when necessary.

Breaking it down to the most basic building blocks is the best way to make it completely extensible in future, however it's also the way to make it very complex, so it depends what you need to achieve in future.

Example in pseudo-code:

var world = new Territory {
    Name = "World",
    Children = []
};

var china = new Territory {
    Name = "People's Republic of China",
    Parents = [world],
    Children = []
};

var eu = new Union {
    Name = "European Union",
    Members = [],
    Relationships = [],
    Purposes = [UnionPurpose.Trade, UnionPurpose.Political]
};

var euChinaRelationship = new TradeRelationship {
    Participants = [eu, china]
};

eu.Relationships.Add(euChinaRelationship);
china.Relationships.Add(euChinaRelationship);

var europe = new Territory {
    Name = "Europe",
    Parents = [world],
    Children = []
};

var britain = new Territory {
    Name = "Britain",
    Parents = [europe],
    Children = []
};

var conservatives = new Union {
    Name = "Conservative Party",
    Members = [],
    Purposes = [UnionPurpose.Political]
};

var ukPrimeMinisterRole = new Role {
    Name = "Prime Minister",
    Responsibilities = ["Leading the government", "Causing trouble :P"],
    BeganAt = "24 July 2019",
    EndedAt = "12 December 2019"
};

var bojo = new Person {
    Name = "Alexander Boris de Pfeffel Johnson",
    Roles = [ukPrimeMinisterRole]
};

conservatives.Members.Add(bojo);
britain.Government = conservatives;

europe.Children.Add(britain);
eu.Members.Add(britain);

For a schema of:

abstract Entity {
    Name: string;
}

Person : Entity {
    DOB: DateTime;
    DOD: DateTime;
    Roles: Role[];
    Relationships: Relationship[];
}

Role {
    Name: string;
    Responsibilities: string[];
    BeganAt: DateTime;
    EndedAt: DateTime;
}

Relationship {
    Reciprocal: boolean;
    Participants: Entity[];
}

TradeRelationship : Relationship {
    Reciprocal = true;
    override Participants: Territory[];
}

Territory : Entity {
    Parents: Territory[];
    Children: Territory[];
    Relationships: Relationship[];
    Government: Union;
}

Union : Entity {
    Members: Entity[];
    Purposes: UnionPurpose[];
    Relationships: Relationship[];
}

enum UnionPurpose {
    Political,
    Military,
    Trade
}

It could take a long time to map this out, but the world is pretty complex.

-1

You ask for an OOP design, what you need to do first is model your data. OOP is a lot more than creating a bunch of classes because your language doesn’t have free functions.

If you don’t need polymorphism or inheritance, then you don’t need an OOP design.

-3

Neither Country nor World inherits from the other; rather, both inherit from an abstract entity with shared behavior as follows:

public abstract class AbstractGeographicalEntity {
  // Shared behavior and attributes of World and Country
}
public interface Country extends AbstractGeographicalEntity {
   private String name;
   private Leader leader;
   private Object flag;
   private Collection<Country> friends;
   private Collection<Country> supports; // a collection of countries supported by the country
   // And so on... 
}
public Class USA implements Country {
   // Implements Country interface with data specific to USA
}
public Class World extends AbstractGeographicalEntity {
   private Collection<Country> countries;
}

In plain English: World has a collection of Countries. Countries have shared behavior via a Country interface. Shared behavior of both Country and World lives in an abstraction above the two.

Answering your questions:

1) Find the country with most supporters: Iterate over the list of countries in World and find the one where Country.friends is largest

2) Future extensions for succession and so on - Leader class (could be President or something else) can be built out here

1
  • 7
    Why would each country have a separate class? Do you expect multiple instances of USA? Why have a base class at all when there is no common interface.
    – Sopel
    Commented Nov 5, 2019 at 16:53
-3

Placing a single Map header file with all the known countries in one place and planning that a new country may exist to succeed some place value "effort" for your initial list to conjure up this country in full, without letting it brain storm a new limb, you should be able to process that countries have to belong to each other in some way without asking it to remove itself from somewhere else. This mskes it possible that you discovered it and the wirld remains the same and it won't 8nclude the changes pccurred but rather that some exchange provoked it...instead of making a new world where it must check all variables aren't assumed to only graph parts and may need to assign itself existing values, as a limb would the heart. It was just covered by that change and that may still exist there for the initial mode to actually keeping track of what's whole in that country to remain intact before the actual destruction phase of any data continues and or occurs. So that the world part takes nothing out of the planet part and the territories leaf off of the approach rather than assume the identities are in a world of their own.

Good scratch on that to actually kill any attempts to usurp throne. Really makes a difference having a grateful testament rather than something "modern" holding it up to require its help when it could just be offering and not necessarily forcing anyone to do any such or ordinary or modern thing. Such would be a good parameter notation device, I think. For this.

Kind of like giving the world class a "main" flag, literal country flag, and making sure that never comes back null.

Not the answer you're looking for? Browse other questions tagged or ask your own question.