0

I have a project where I develop a software which consists of a CORE which is in a separate project, developed separately, and a CLIENT which's base is the CORE but adds to the functionality with modules and whatnot. But also extends the System and other CORE classes. This is required because there are multiple SUBCLIENTs which's base is the CLIENT.

So the inheritance structure looks like the following: CORE -> CLIENT -> SUBCLIENT1, SUBCLIENT2, SUBCLIENT3...

What my goal is, whenever I update my CORE, it bubbles down to CLIENT and then to all SUBCLIENTS. And whenever I update the CLIENT, it bubbles down to SUBCLIENTS. This has been achieved, BUT there is a MANAGEMENT project which uses the CORE but NOT the CLIENT. Thus there are only 2 depths to that project.

My question is, how can I implement an inheritance structure which can handle as many depths as I want?

I give you an example:

There is currently this structure for the SystemBase class in a SUBCLIENT:

  • (CORE) SystemBase.php
  • (CLIENT) SystemClient.php (extends SystemBase)
  • (SUBCLIENT) SystemVirtual.php (extends SystemClient)

This means that SystemBase has a basic functionality, upon which the SystemClient builds its own. But then on the third level, SystemVirtual adds modifications to these classes and their functionality. But what happens at Management? It would mean that SystemClient should not exist, because there are only 2 levels not 3.

But the problem is, I don't know how to write my System's structure so the CLIENT, MANAGEMENT and SUBCLIENTS can override it if needed. Because if I write my CORE to use SystemVirtual, then I hardcode 3 levels, and I can not inject more levels if needed.

I want my last level class to be used everywhere. Even in code written in the CORE which is level 1.

Any thoughts on the problem?

Thanks in advance!

  • 1
    I don't understand what you are asking. I guess this is the case because your class names tell me absolutely nothing about the classes. On this first glance it looks like you are abusing inheritance to share functionality. If inheritance gets so complex that you want to use subclass methods from a parent, its usually time for a serious refactoring and/or redesign of that part of the codebase. – marstato Apr 15 at 15:18
0

I want my last level class to be used everywhere. Even in code written in the CORE

CORE should know nothing whatever about any of the Classes "below" it. As far as it is concerned, it is self-contained and does whatever it does. If some other class chooses to override the bits of CORE that CORE allows to be overridden, that's on them, not on CORE.

If CORE has to "know" (ask) anything about the "lower" classes to work out what to do, then your design is wrong. If your code in CORE is testing the type of the object and behaving differently based on that, then your design is wrong.

One of the better ways of working with Inherited Objects is this:

Tell, don't ask.

Tell the object (or, with inheritance, yourself) to do something; the object in question will either

  • do whatever you asked, or
  • ignore you, because it just doesn't do that, or
  • throw an Exception because it can't (or won't) do that and thinks that you ought to know about it.

That said, there is nothing to stop MANAGEMENT extending (subclassing) CORE directly ...

class MANAGEMENT 
   extends CORE 

... but CORE cannot reference anything defined solely in the MANAGEMENT (or any other subclass) directly. That tightly binds the two classes together, which isn't what you want.

If there is common functionality between CLIENT, SUBCLIENT and MANAGEMENT, then you may need to look at using Interfaces to define those behaviours, which can be attached at any level in your class hierarchy and used by CORE.

0

Inheritance doesn't solve every problem. It sounds like the problem you are looking to solved may be better solved with some form of dependency instead of subclassing. Instead of having multiple levels of inheritance, shared functionality can be put into a separate class that is outside of the inheritance hierarchy and then passed in as a parameter to the constructor. If you prefer Composition over inheritance, then you no longer are shackled by the limitations of base classes necessarily being unaware of their concrete implementations.

My suggestion: identify related functionality that may be necessary in your CORE that can be cut out into a brand new class that has no sub-classes. Then when you are constructing objects inherited from CORE, pass this object in as a parameter to the constructor. When you arrive at a point where the functionality in that class needs to change in some circumstances, then you can create an interface that defines the contract of what that type needs to adhere to. Multiple classes can implement an interface, so what you do is create another class (that has no inheritance) that implements the contract in the different way.

Just so you can have a concrete example: I like to save things. Sometimes I like to save things to a database, sometimes I like to save things to a file, sometimes I like to save things to a third party service and I don't care how they save the thing.

interface Saver {
  public function save();
}

class CORE
{
  protected Saver saver;

  public __construct(Saver mySaver)
  {
    this->saver = mySaver;
  }

  public save()
  {
    this->saver->save();
  }
}

class FileSaver implements Saver
{
  public function save(Stuff myStuff)
  {
    // implementation for saving stuff to a file
  }
}

class DatabaseSaver implements Saver
{
  public function save(Stuff myStuff)
  {
    // implementation for saving stuff to a database
  }
}

class ThirdPartySaver implements Saver
{
  public function save(Stuff myStuff)
  {
    // implementation for saving stuff to the third party
  }
}

With this, your different clients, subclients, and management objects can all have different implementations of Saver given to them:

Client client1 = new Client1(new FileSaver);
SubClient client2 = new SubClient(new DatabaseSaver);
Management mgmt = new Management(new ThirdPartySaver);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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