Search code examples
classoopinner-classesencapsulationcomposite

Private nested classes - are they necessary for composition?


This is my naive thought process. I appreciate when anyone points out any discrepancies.

I know that in Java it is possible to create private nested classes. But in PHP (calling itself an "OOL") no such thing is easily possible.

However, there may be an instance where I need to use a helper class within only one other class.

This naturally led me to consider using composition about which I think its supposed to actually solve this kind of a problem, for the following reason:

Wikipedia:

  • It is only called composite, if the objects it refers to are really its parts, i.e. have no independent existence.
  • Aggregation differs from ordinary composition in that it does not imply ownership. In composition, when the owning object is destroyed, so are the contained objects. In aggregation, this is not necessarily true.

So since in composition the components aren't supposed to exist without the other composite object, I assume there is basically only one way of accomplishing this by using private nested classes, otherwise I will need to have the Component class visible, making it instantiable also somewhere else, violating the creation/destruction-of-components-within-the-composite-class rule.

Now I came across PHP where it is not possible at all to create nested classes (or maybe with some magic) which may lead to a question why do we need nested classes at all?

Compelling reasons for using nested classes include the following:

  • It is a way of logically grouping classes that are only used in one place: If a class is useful to only one other class, then it is logical to embed it in that class and keep the two together. Nesting such "helper classes" makes their package more streamlined.

  • It increases encapsulation: Consider two top-level classes, A and B, where B needs access to members of A that would otherwise be declared private. By hiding class B within class A, A's members can be declared private and B can access them. In addition, B itself can be hidden from the outside world.

  • It can lead to more readable and maintainable code: Nesting small classes within top-level classes places the code closer to where it is used.

So in my opinion, as nested classes increase encapsulation (and thus allowing for implementation of a composite concept) it should be always possible to create a nested class in a proper OOL.

I also looked up the definition of an OOP and it only mentions the support for Encapsulation, Abstraction, Inheritance, Polymorphism concepts.

OOP

Encapsulation means that the internal representation of an object is generally hidden from view outside of the object’s definition.

Abstraction is the development of classes, objects, types in terms of their interfaces and functionality, instead of their implementation details. (e.g. instead or creating a single sequence of commands to work with a radius and points we would abstract a concept of a circle. So we define a class Circle and a its attributes/functions in such a way that it reflects this abstract concept.)

Inheritance is supposed to be the is-a relationship between abstractions (allowing for code reuse).

Polymorphism allows for overriding and overloading methods.

A guy asked a question here which actually complies exactly with my understanding of the problem and IMHO received quite inaccurate answer, being told to be really confused and being provided with a code sample which IMO is not a proper composition, as it is not really possible in PHP. In addition someone may argue that composition isn't about inner classes.

So am I understanding something really wrong?


Solution

  • In wikipedia, there's also the following example of composition without using private nested classes.

    class University
    {
      std::vector<Department> faculty; //this is composition
      std::vector<People*> people; //this is aggregation
    
      University()  // constructor
      {
        // Composition: Departments exist as long as the University exists
        faculty.push_back(Department("chemistry"));
        faculty.push_back(Department("physics"));
        faculty.push_back(Department("arts"));
      }
    };
    

    For a true composition we don't have to make the whole class private as long as we treat instances of departments appropriately, i.e. we need to make sure that all departments will actually get deleted when the university ceases to exist.

    An analogous ways of implementing different compositions in JavaScript would be as follows:

    /*this is a component's "class"*/
    function Text(txt){
       this.txt = txt;
    }
    
    /*this is a composite's "class"*/
    function Hello(begining){
       /*public object*/
       this.begining = begining;
    
       /*this is like making an instance from a nested function which is a composite*/
       var Middle = new (function Middle(txt){
           this.txt = txt;
       })(" - ");
    
       /*private object - also a composite, even though it's made of the public class Text*/
       var Ending = new Text("that's all.");
    
       /*The use of the private property Ending*/
       this.Say = function(){
          var msg = this.begining.txt + Middle.txt + Ending.txt;
          console.log(msg);
       }
    }
    /*
       This txt variable will be the "begining" text. It could still be a composite, but
       only if it's not used in another composite when purpose is to be 
       "an untransferable part" and not a "visitor".
    */
    var txt = new Text("Dan");
    var say1 = new Hello(txt);
    var say2 = new Hello(new Text("To be or not to be"));
    
    say1.Say()  /*Dan - that's all.*/
    say2.Say() /*To be or not to be - that's all.*/
    

    But even the transferability is often a neglected rule when people see "cars" having "wheels" as parts (rather then "visitors")

    Instead of perhaps "neural network" and "neurons" or "forest" and "trees". Trees don't get replanted to a different forest so often.

    And since the wheels can still be understood as parts of a composite, it doesn't have to differ from aggregation in code.