Home > Enterprise >  Object Oriented Programming: Subclasses or Enum
Object Oriented Programming: Subclasses or Enum

Time:10-12

I am making a simple turn-based farming simulator game where players make choices whether to buy land or crops and have turn-count based growing. Different crops grow for different times, and have different purchase prices and sale values. Objective is to be the first to reach a dollar amount.

My question is how to develop these crops programmatically. I currently have each crop variation as a subclass of a Crop, however, this leads to a lot of redundancy (mostly different field/attribute values, or image paths). Since these objects are very similar save for some values, should they be subclasses, or would it be better practice to make one class Crop with an Enum of type and use logic to determine the values it should have?

Superclass Crop

  • subclass Wheat
  • subclass Corn
  • subclass Barley

Or

Crop.Type = CropType.Wheat

if(this.Type == CropType.Wheat) { return StockMarket.Wheat_Sell_Value; }

else if(this.Type == CropType.Corn) { return StockMarket.Corn_Sell_Value; }

CodePudding user response:

The answer depends on the difference in functionality between the crop types. The general rule is to avoid unnecessary complexity where possible and inheritance should be used sparingly because it introduces hard dependencies.

So if all crops are functionally similar and only differ by their attribute values then you would want to use a single class for crop, but if your game logic demands the crop types to behave very differently and/or carry very different sets of data, then you may want to consider creating separate structures.

If inheritance would be the best choice in case you need separate structures cannot be answered without knowing the exact details of your game world either. Some alternatives could consider are:

  1. interfaces,
  2. structs or
  3. a functional programming approach where the crops only exist as primitives being passed around functions.

CodePudding user response:

If you make a single crop class it risks becoming very large and unwieldly, especially if you want to add a new crop type you'll have to update the 100's of if statements littered through your code (e.g. if(this.Type == CropType.Wheat) { return StockMarket.Wheat_Sell_Value; }).

To echo @oswin's answer, use inheritance sparingly. You are probably ok using a base-class with a few "dumb" properties, but be especially careful when adding anything that implements "behaviour" or complexity, like methods and logic; i.e. anything that acts on CropType within Crop is probably a bad idea.

One simple approach is if crop types all have the same properties, but just different values; and so crop instances just get acted on by processes within the game, see below. (Note: If crops have different properties then I would probably use interfaces to handle that because they are more forgiving when you need to make changes).

// Crop Types - could he held in a database or config file, so easy to add new types.
// Water, light, heat are required to grow and influence how crops grow.
// Energy - how much energy you get from eating the crop.
Name:Barley, growthRate:1.3, water:1.3, light:1.9, heat:1.3, energy:1.4
Name:Corn, growthRate:1.2, water:1.2, light:1.6, heat:1.2, energy:1.5
Name:Rice, growthRate:1.9, water:1.5, light:1.0, heat:1.4, energy:1.8

The crop type values help drive logic later on. You also (I assume) need crop instance:

class CropInstance() { CropType Crop { get; set; } double Size { get; set; } double Health { get; } }

Then you simply have other parts of your program that act on instances of Crop, e.g:

void ApplyWeatherForTurn(CropInstance crop, Weather weather)
{
  // Logic that applies weather to a crop for the turn.
  // E.g. might under or over supply the amount of water, light, heat 
  // depending on the type of crop, resulting in 'x' value, which might 
  // increase of decrease the health of the crop instance.

  double x = crop.WaterRequired - weather.RainFall;
  // ...

  crop.Health = x;
}

double CurrentValue(CropInstance crop)
{
  return crop.Size * crop.Health * crop.Energy;
}

Note you can still add logic that does different things to different crops, but based on their values, not their types:

double CropThieves(CropInstance crop)
{
  if(crop.health > 2.0 & crop.Energy > 2.0)
  {
    // Thieves steal % of crop.
    crop.Size = crop.Size * 0.9;
  }
}
  • Related