0

Say I have a class to model my customers

class Customer 
{
    public static customerType = 'customer';
}

And a subclass class CorporateCustomer extends Customer to model my corporate customers. Presently I have three subclasses

class AlphaCustomer extends CorporateCustomer {...}
class BetaCustomer  extends CorporateCustomer {...}
class GammaCustomer extends CorporateCustomer {...}

Each subclass of Customer overwrites the customerType property. This property is used when my model is loaded into my database.

Of course, as my business evolves new subclasses of CorporateCustomer might be added.

Now in another part of my application, I have a variable called currentCustomerType and I need to determine if there exists a subclass c of CorporateCustomer such that c::customerType is equal to currentCustomerType.

How would you do this?

11
  • 1
    I'm not sure to understand your question. If you need to know if a JSON string is a representation of a CorporateCustomer class, wouldn't you just deserialize it as such, and see if the deserialization works? Apr 16, 2020 at 19:20
  • 1
    Is this Java? Maybe you could try the reflection library mentioned here to examine the classes themselves, rather than a custom property. Apr 16, 2020 at 20:29
  • 4
    You're asking how to proceed with a design which is already seriously flawed. The answer is to back up, describe the actual problem you're trying to solve as if you hadn't written any code at all. It's frustrating, but it will let people help you and maybe result in a more effective base design.
    – Nathan
    Apr 16, 2020 at 21:53
  • 1
    Am I misunderstanding the question, or is this just asking about the factory pattern? I'd hardly call that a "seriously flawed" design - it's the usually way to deserialize JSON (for example) in a polymorphic way.
    – Errorsatz
    Apr 16, 2020 at 22:33
  • 1
    Perhaps you could interpret seriously flawed as a potential source of bugs? The most common mistake might be to add a new subclass but forget to update the logic which finds subclasses based on type.
    – Dan Wilson
    Apr 16, 2020 at 22:38

6 Answers 6

5

Despite the OO tags you put on your question, what you’re trying to do isn’t object oriented. If it was you’d be hiding types not asking about them.

Now sure sometimes you need to have relationships between things that you can query. But that’s not types. That’s containers. Use those for this. Doing this to the type system just makes a mess.

6
  • Well what I currently have is object oriented, and what I'd need to achieve you just cast as not object oriented. I guess I have an object oriented setup, and I'm trying to figure out how to achieve something given this setup. Hopefully as nicely as possible. And I agree, making a mess with types is something I'm trying to avoid, so I guess I'm looking for alternatives to that.
    – gen
    Apr 16, 2020 at 20:00
  • 5
    Work your requirement. Your question is imposing a solution. Figure out what you really need to achieve. Right now all you’re asking is how to do it this way. If that’s all you ask all I can say is don’t do it that way. Apr 16, 2020 at 20:02
  • There's only one question I'm seeking to find an answer for and it doesn't impose a solution at all. It asks "How would you maintain a list of the customerType property on the various subclasses of the CorporateCustomer class?"
    – gen
    Apr 16, 2020 at 20:06
  • 1
    Don’t call it object oriented. OO code doesn’t have a type property. Apr 16, 2020 at 20:08
  • "What you’re trying to do isn’t object oriented." - that is probably correct, but "object oriented" is not automatically "the only right way". There may be a situation which may justify exactly what the OP has written.
    – Doc Brown
    Apr 17, 2020 at 20:50
4
  • Avoid inheritance, try composition.
  • If you must be able to detect the type, perhaps try virtual members, that can be overridden in extending classes.

There is a problem, but it's not what you might think. You are asking how to abuse the type system, and the answer you will (correctly) be getting is not to do so. The actual problem is that this is the short answer and the long answer is pretty long!

"How would you maintain a list of the customerType property on the various subclasses of the CorporateCustomer class?"

Your insistence means that the long answer has to be even longer. Well, I won't try to give the long answer, but at least, let me try to set some things straight.

There are many ways you can use to abuse the type system so that you can have your way. If you insist on doing that (maybe you just need a very quick fix), then:

//c#, but easily portable to java

class Customer
{
    protected static List<string> m_availableCustomerTypes = new List<string>();

    public virtual string CustomerType => "Plain Customer";

    static Customer()
    {
        m_availableCustomerTypes.Add("Plain Customer");
    }

    //Gets you all the available customer types. Exposes the protected variable
    //but only as an IEnumerable.
    public static IEnumerable<string> AllCustomerTypes => m_availableCustomerTypes;
}

class CorporateCustomer : Customer
{

    public override string CustomerType => "Corporate Customer";

    static CorporateCustomer
    {
        m_availableCustomerTypes.Add("Corporate Customer");
    }
}

//etc...

I.e. you can create a static list in the root class of the Customer hierarchy, and not forget to "register" the customer type inside each static constructor, by adding it to the base-most list. This way, you can iterate the AllCustomerTypes list from any place within your codebase and you should have all the types, just don't forget to add them in each derived class' static constructor.

This solution officially sucks, but a man's gotta do what a man's gotta do, right? Well, this is the tip of the iceberg. The rest of the long answer I was talking about earlier has to do with why this is a terrible idea. And even if it never ever ends up causing you any trouble, it remains a terrible idea. Why?

  • It accustoms you to a very bad practice that
  • robs you of the true abstraction power of object-oriented programming, which lies in
  • not needing to know implementation details of the objects you are juggling in the location you are juggling, as long as
  • you have clearly and properly defined interaction semantics (cleanly defined interfaces for your types).

The moment you need to check the type of an object, you are creating a location within the codebase, which will be excessively fragile. The answer I provided above fails if someone tries to extend your codebase and forgets to "register" the customer type. So only you know the "secrets" of your classes and, who knows, you may forget it in the future. How "empowering" is that? The idea is to let the type system remind you when you are trying to do something you did not intend to in the first place.

If you happen to ask what you should have done, the answer is, skip inheritance, use composition. Why do you need 10 strongly-typed Customer alternatives anyway? At the very least, use an enum.

3

If I'm understanding correctly, what you're looking for is the factory pattern. You have plain data (JSON in this case), and you want to deserialize it in a polymorphic way. The determining factor in this case is a static field, but that's incidental.

So you'd have a factory method like:
Customer CreateCustomer(string json);

And that would return the appropriate subclass of Customer. How? Usually a switch statement. But you could also use reflection to scan through the subclasses, if you don't mind the performance cost.

5
  • The factory pattern is exactly what I was going to propose.
    – Dan Wilson
    Apr 16, 2020 at 22:35
  • A factory will give you the JSON of 1 subtype. It will not give you a list of all subtypes. Apr 16, 2020 at 23:28
  • I made some changes to my question to better suit @Arseni's taste, and I'm now content with getting one subtype even. So I find this answer valuable, thanks Errorsatz for posting.
    – gen
    Apr 17, 2020 at 7:01
  • @gen, a factory will give you an object of a Customer subclass based on the arguments you pass in and the classes know to the factory, or an error (or whatever you decide) if no known subclass can be instantiated from the arguments. Does that meet your needs? Apr 17, 2020 at 12:22
  • The factory will also need to be a (possibly speculative or proactive) class loader. It will need to load the class (for the Customer subtype) even if that class hasn't been loaded yet. To do so, the factory will need to either attempt to load a class given a string, or by proactively scanning a specific class-file folder for new class-files or changes to jars.
    – rwong
    Apr 19, 2020 at 12:56
1

... I need to determine if there exists a subclass c of CorporateCustomer...

This sounds like a cross-cutting concern, your problem spans across the whole application. These types of problems are addressed by aspect-oriented programming (AOP). To get all subclasses of a certain class, you would typically query the class as CorporateCustomer+. In the aspect advice you can subsequently store a (preferably weak reference) Map<String, Class>, where the keys are customerType

1

Now in another part of my application, I have a variable called currentCustomerType and I need to determine if there exists a subclass c of CorporateCustomer such that c::customerType is equal to currentCustomerType.

There is a simple and straightforward solution to this problem, which does not need "reflection" and works in most OO programming languages:

  • in the part of the application where the subclasses are defined, make a list of CorporateCustomer objects which holds exactly one "prototypical" object of each available subclass.

  • pass that list to that "other part" of your application where currentCustomerType exists

  • now simply iterate over the list to check if there is an instance c in the list for which c.customerType matches currentCustomerType.

If you found such an instance c and use it, for example, to create other instances of the same type by "cloning", then you have rediscovered the well known Prototype pattern.

Of course, there are some assumptions in my answer, first and foremost that there is a well-defined list of subtypes in some area of your code from which it is not too hard to create and maintain the mentioned list. You have to check for yourself if your system meets this condition.

0

You might design your software with plugins in mind (e.g. using dlopen(3) on Linux, or Java class loader if using the JVM), to ease its customization or extendability.

If you have plugins (or any other runtime metaprogramming facilities) you cannot dynamically know all the subclasses of a given class, because that set will vary with time, since a freshly loaded plugin can define new classes.

Dynamically knowing all the subclasses of a superclass

... it is not possible if the code changes (e.g. by plugins being loaded) at runtime. Unless your programming language object model is as powerful as the ObjVLisp model.

You might get inspiration from Qt object model and metaclasses.

Your many variant of customers issue smells like a sum type.

Read Abadi and Cardelli A theory of objects book. Notice that typing and objects are concepts in tension (and have very different definitions: Ocaml object model is not Java or Scala or Agda or CLOS one). Be aware of agent-based programming, and of multiple dispatch concepts.

With multi-threading and garbage collection, things become even more complex. Read also the GC handbook.

See also the RefPerSys project, closely related to your concerns.

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