22

Lets say I have a Enemy class, and the constructor would look something like:

public Enemy(String name, float width, float height, Vector2 position, 
             float speed, int maxHp, int attackDamage, int defense... etc.){}

This looks bad because the constructor has so many parameters, but when I create an Enemy instance I need to specify all of these things. I also want these attributes in the Enemy class, so that I can iterate through a list of them and get/set these parameters. I was thinking maybe subclassing Enemy into EnemyB, EnemyA, while hardcoding their maxHp, and other specific attributes, but then I'd lose access to their hardcoded attributes if I wanted to iterate through a list of Enemy (consisting of EnemyA's, EnemyB's, and EnemyC's).

I'm just trying to learn how to code cleanly. If it makes a difference, I work in Java/C++/C#. Any point in the right direction is appreciated.

4
  • 6
    There is nothing bad about having one constructor that binds all attributes. In fact, in some persistence environments, it is required. Nothing says you can't have multiple constructors, perhaps with validity checking method to be called after doing the piece-wise construction. Mar 10, 2014 at 19:08
  • 1
    I'd have to question if you ever intend to construct Enemy objects in code using literals. If you don't, and I don't see why you would, then build constructors that pull the data from a database interface, or a serialization string, or ...
    – Zan Lynx
    Mar 11, 2014 at 19:01
  • Named Parameter idiom Mar 11, 2014 at 20:29

5 Answers 5

60

The solution is to bundle up the parameters into composite types. Width and Height are conceptually related - they specify the dimensions of the enemy and will usually be needed together. They could be replaced with a Dimensions type, or perhaps a Rectangle type that also includes the position. On the other hand, it might make more sense to group position and speed into a MovementData type, especially if acceleration later enters the picture. From context I assume maxHp, attackDamage, defense, etc also belong together in a Stats type. So, a revised signature might look something like this:

public Enemy(String name, Dimensions dimensions, MovementData movementData, Stats stats)

The fine details of where to draw the lines will depend on the rest of your code and what data is commonly used together.

4
  • 21
    I would also add that having so many values might indicate violation of Single Responsibility Principle. And grouping values into specific objects is first step into separating those responsibilities.
    – Euphoric
    Mar 10, 2014 at 19:53
  • 2
    I don't think that the list of values is a SRP problem; most of them are probably intended for base class constructors. Each class in the hierarchy can have a single responsibility. Enemy is just the class that targets the Player, but their common base class Combatant needs the fight stats.
    – MSalters
    Mar 11, 2014 at 13:16
  • @MSalters It doesn't necessarily indicate an SRP problem, but it could. If he needs to do enough number-crunching, those functions could find their way into the Enemy class when they should either be static/free functions (if he uses Dimensions/MovementData as plain old data containers) or methods (if he turns them into abstract data types/objects). As an example, if he hadn't already created a Vector2 type, he might've ended up doing vector math in Enemy.
    – Doval
    Mar 12, 2014 at 11:30
  • Guys, let me remind you that attributes are not the same as dependencies. All parameters of the constructor here are attributes and it is a fact of life that some entities will have lots of properties and those must be defined somehow. You cannot violate the SRP when you only get attributes in the constructor, it's dependencies that you need to be careful of. The name, dimensions, etc. are only attributes describing the class, a dependency would be some external (preferably interface-based) object that the Enemy actually depends upon to exhibit some behavior. Aug 6, 2023 at 19:19
25

You might want to take a look at the Builder pattern. From the link (with an examples of the pattern versus alternatives):

[The] Builder pattern is a good choice when designing classes whose constructors or static factories would have more than a handful of parameters, especially if most of those parameters are optional. Client code is much easier to read and write with builders than with the traditional telescoping constructor pattern, and builders are much safer than JavaBeans.

4
  • 4
    A short code snippet would be helpful. This is a great pattern for building complicated objects or structures with various inputs. You could also specialize the builders, as EnemyABuilder, EnemyBBuilder, etc. that encapsulate the various shared properties. This is kind of the flip side of the Factory pattern (as answered below), but my personal preference is for Builder.
    – sea-rob
    Mar 10, 2014 at 19:28
  • 1
    Thanks, both the Builder pattern and Factory patterns look like they'd work well with what I'm trying to do overall. I think a combination of Builder/Factory and Doval's suggestion might be what I'm looking for. Edit: I guess I can only mark one answer; I'll give it to Doval since it answers the topic question, but the others are equally as helpful to my specific problem. Thank you all.
    – Travis
    Mar 10, 2014 at 22:30
  • I think it is worth noting that if your language supports phantom types, then you can write a builder pattern that enforces that some/all of the SetX functions get called. It also allows one to ensure that they get called only once too (if desired). Mar 10, 2014 at 23:23
  • 1
    @Mark16 As mentioned in the link, > The Builder pattern simulates named optional parameters as found in Ada and Python. You mentioned you also use C# in the question, and that language does support named/optional arguments (as of C# 4.0), so that may be another option.
    – Bob
    Mar 11, 2014 at 4:25
5

Using subclasses to preset some values is not desirable. Only subclass when a new type of enemy has different behavior or new attributes.

The factory pattern is usually used to abstract over the exact class used, but it can also be used to provide a templates for object creation:

class EnemyFactory {

    // each of these methods is essentially a template for a kind of enemy

    Enemy enemyA(String name, ...) {
        return new Enemy(name, ..., presetValue, ...);
    }

    Enemy enemyB(String name, ...) {
        return new Enemy(name, ..., otherValue, ...);
    }

    Enemy enemyC(String name, ...) {
        return new EnemySubclass(name, ..., otherValue, ...);
    }

    ...
}

EnemyFactory factory = new EnemyFactory();
Enemy a = factory.enemyA("fred", ...);
Enemy b = factory.enemyB("willy", ...);
1
  • In my very humble opinion it's a kind of misuse of the Factory Method Patter. Since the creating of objects (or, to be more precise, the concern / the responsibility for creating), the actual core job of the factory, is here (at least partially) going back to the object itself. And the "templating" can usually (depending of the language, of course) be implemented as / replaced by multiple constructors (overloading).
    – automatix
    Dec 3, 2020 at 18:02
0

I would reserve sub classing to classes that represent object that you might want to independently use, e.g. character class where all characters, not just enemies have name, speed, maxHp, or a class to represent sprites that have a presence on screen with width, height, position.

I don't see anything inherently wrong with a constructor with a lot of input parameters but if you want to split it up a bit then you could have one constructor that sets up most of the parameters and another (overloaded) constructor that can be used to set specific ones and have others set to default values.

Depending on what language you choose to use, some can set default values for the input parameters of your constructor like:

Enemy(float height = 42, float width = 42);
0

A code example to add to Rory Hunter's answer (in Java):

public class Enemy{
   private String name;
   private float width;
   ...

   public static class Builder{
       private Enemy instance;

       public Builder(){
           this.instance = new Enemy();
       }


       public Builder withName(String name){
           instance.name = name;
           return this;
       }

       ...

       public Enemy build(){
           return instance;
       }
   }
}

Now, you can create new instances of Enemy like this:

Enemy myEnemy = new Enemy.Builder().withName("John").withX(x).build();
1
  • 1
    Programmers is tour conceptual questions and answers are expected to explain things. Throwing code dumps instead of explanation is like copying code from IDE to whiteboard: it may look familiar and even sometimes be understandable, but it feels weird... just weird. Whiteboard doesn't have compiler
    – gnat
    Mar 11, 2014 at 14:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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