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.

Christophe
  • 81,699
csavvy
  • 345

11 Answers11

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.

Simon B
  • 9,772
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
};
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?).

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.

Christophe
  • 81,699
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.

Davislor
  • 1,563
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
};
skott
  • 509
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.

bracco23
  • 429
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.

Dom
  • 570
-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.

jmoreno
  • 11,238
-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

8t12c7081
  • 101
-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.