best approach to code hierarchical classes ?

someone someone at somewhere.com
Wed Jun 9 17:53:18 UTC 2021


On Tuesday, 8 June 2021 at 02:37:44 UTC, someone wrote:
> On Tuesday, 8 June 2021 at 02:05:27 UTC, Paul Backus wrote:

> Your module and class are both named `classComputers`, with an 
> `s` at the end. You should change one of the to have a 
> different name so that there's no ambiguity.

Although I am still not being able to solve the import issue for 
the child code on my nested-classes (low priority by the time 
being), I have now a more pressing issue.

But first and foremost: almost sure you'll find my code style 
noisy given the heavily-used attribue/function properties, but 
this is solely to remind me how things work in a new language, 
what will be obvious to an experienced D developer (default 
attributes) it is not obvious to me right now ... spare me the 
style, will you ?

I moved all the boilerplate code to manage the collection to 
classCollectionItems which also has a nested child 
classCollectionItem (since I'll be widely using them as the basis 
for hierarchical class models) and from then on I inherit the 
class on my final classComputers/classComputer code which is my 
first test-bed app in D.

Before moving the code to the new generic abstract classes the 
code below worked flawlessly, and still works albeit for a minor 
detail:

I now **have an issue accessing the collection** (previously 
defined in the base classCollectionItems) through my derived 
classComputers class: please, look for the comment block marked 
with +++ below.

The base classCollectionItems will always have a collection named 
items, and each derived class I will make from it should have a 
reference to it renamed accordingly; eg: computers in the case of 
my classComputers class. Later I will plan to make an interface 
to hide all the inner details and hide the items property of the 
base class once and for all, leaving each new derived class' own 
renamed property publicly accessible). So when the issue first 
appeared on my new refactored code I **started playing with alias 
to no avail** -and even making a new computers property 
initialized with items (but then I figured it out that this will 
be copying the array -not referencing it, so this will be a 
no-go).

Some advice please ?

```d
#!/bin/dmd

module dmclassescomputers;

import std.string;

abstract private class classCollectionItems {

    classCollectionItems lhs;
    classCollectionItems rhs;

    int opApply(int delegate(classCollectionItem) dg) { /// 
boilerplate code to handle the class's default collection

       int lintResult = 0; /// must find a better name

		foreach (lobjItem; items) { /// looping over the computers 
starting in current node

			lintResult = dg(lobjItem); /// passing single object to the 
loop body

			if (lintResult != 0) { break; }

		}

       if (lintResult != 0 && lhs ! is null) { lintResult = 
lhs.opApply(dg); } /// recursing child nodes
       if (lintResult != 0 && rhs ! is null) { lintResult = 
rhs.opApply(dg); } /// recursing child nodes

       return lintResult;

    }

    public @property classCollectionItem[] items; alias items 
this; /// ie: default property

    final public @property const long count0() { return 
this.items.empty ? 0L : this.items.length - 1L; }
    final public @property const long count1() { return 
this.items.empty ? 0L : this.items.length; }
    final public @property const long count() { return 
this.count1; }
    final public @property const bool empty() { return 
this.items.empty; }

    abstract public bool add(in string lstrID) { return false; }
    abstract public bool remove(in string lstrID) { return false; }
    abstract public bool removeAll() { return false; }

    abstract class classCollectionItem {

       private ulong pintPosition0 = 0L; /// keep in mind that 
array positions are zero-based

       final public @property const ulong position0() { return 
this.pintPosition0; }
       final public @property const ulong position1() { return 
this.pintPosition0 + 1L; }

       final public @property const ulong countAbove() { return 
this.outer.items.empty ? 0L : this.pintPosition0; }
       final public @property const ulong countBelow() { return 
this.outer.items.empty ? 0L : this.outer.length - 
this.pintPosition0 - 1L; }

       /// eg: for position0=0 → countAbove=0=(position0=0) & 
countBelow=2=(length=3)-(position0=0)-1
       /// eg: for position0=1 → countAbove=1=(position0=1) & 
countBelow=1=(length=3)-(position0=1)-1
       /// eg: for position0=2 → countAbove=2=(position0=2) & 
countBelow=0=(length=3)-(position0=2)-1

       final public @property classCollectionItem first() { return 
this.outer.items.empty ? null : this.outer.items[0L]; }
       final public @property classCollectionItem previous() { 
return this.outer.items.empty || this.countAbove == 0L ? null : 
this.outer.items[this.pintPosition0 - 1L]; }
       final public @property classCollectionItem next() { return 
this.outer.items.empty || this.countBelow == 0L ? null : 
this.outer.items[this.position0 + 1L]; }
       final public @property classCollectionItem last() { return 
this.outer.items.empty ? null : 
this.outer.items[this.outer.items.length - 1L]; }

    }

}

final private class classComputers : classCollectionItems {

    private string pstrNetwork;
    final public @property const string network() { return 
this.pstrNetwork; }
    final public @property void network(in string lstrNetwork) { 
this.pstrNetwork = lstrNetwork.strip(); }

    this(
       in string lstrNetwork = null
       ) {

       if (lstrNetwork ! is null) { this.network = lstrNetwork; }

    }

    /// +++ Error: no property `ID` for type 
`dmclassescomputers.classCollectionItems.classCollectionItem`

    /// +++ alias computers = this;
    /// +++ alias computers = this.items;
    /// +++ alias computers = classCollectionItems;
    /// +++ alias computers = classCollectionItems.items; alias 
computers this; /// it seems to me something like this should be 
the correct one but obviously it is not :(
    /// +++ alias computers = classCollectionItems.items[];
    /// +++ public @property classCollectionItem[] computers = 
this.items;

    /// +++ checked: https://dlang.org/spec/declaration.html#alias
    /// +++ checked: http://ddili.org/ders/d.en/alias_this.html

    final public bool add(in string lstrID, in string lstrName) { 
return false; }

    override final public bool add(in string lstrID) { return 
false; }

    override final public bool remove(in string lstrID) { return 
false; }

    override final public bool removeAll() { return false; }

    final class classComputer : classCollectionItem { /// should: 
import dmclassescomputer;

       private string pstrID; final @property const string ID() { 
return this.pstrID; }
       private string pstrName; final @property const string 
name() { return this.pstrName; }
       final public @property void name(in string lstrName) { 
this.pstrName = lstrName.strip(); }

       this(
          in string lstrID,
          in string lstrName = null
          ) {

          this.pintPosition0 = this.outer.items.length;

          if (lstrID ! is null) { this.pstrID = lstrID.strip(); }
          if (lstrName ! is null) { this.pstrName = 
lstrName.strip(); }

       }

    }

    unittest {

       dmclassescomputers.classComputers lobjComputers;

       lobjComputers = new dmclassescomputers.classComputers; 
assert(lobjComputers.network is null);
       lobjComputers = new 
dmclassescomputers.classComputers(null); 
assert(lobjComputers.network is null);
       lobjComputers = new 
dmclassescomputers.classComputers(r"lab"c); 
assert(lobjComputers.network == r"lab"c);

       assert(lobjComputers.count0 == 0);
       assert(lobjComputers.count1 == 0);
       assert(lobjComputers.count == 0);

       bool lbolComputers = false;

       foreach(lobjComputer; lobjComputers) { /// testing access 
to the empty collection: should never get within the body of the 
loop

          lbolComputers = true;

       }

       assert(lbolComputers == false);

       lobjComputers ~= lobjComputers.new classComputer(r"WS1"c, 
r"dell"c); /// adding the first computer

       assert(lobjComputers.count == 1L);
       assert(lobjComputers.count1 == 1L);
       assert(lobjComputers.count0 == 0L);

       with (lobjComputers[0L]) {

          assert(countAbove == 0L);
          assert(countBelow == 0L);

          assert(first ! is null && first.ID == r"WS1"c);
          assert(previous is null);
          assert(next is null);
          assert(last ! is null && last.ID == r"WS1"c);

       }

       lobjComputers ~= lobjComputers.new classComputer(r"WS2"c, 
r"ibm"c); /// adding a second computer

       assert(lobjComputers.count == 2L);
       assert(lobjComputers.count1 == 2L);
       assert(lobjComputers.count0 == 1L);

       with (lobjComputers[1L]) {

          assert(countAbove == 1L);
          assert(countBelow == 0L);

          assert(first ! is null && first.ID == r"WS1"c);
          assert(previous ! is null && previous.ID == r"WS1"c);
          assert(next is null);
          assert(last ! is null && last.ID == r"WS2"c);

       }

       lobjComputers ~= lobjComputers.new classComputer(r"WS3"c, 
r"apple"c); /// adding a third computer

       assert(lobjComputers.count == 3L);
       assert(lobjComputers.count1 == 3L);
       assert(lobjComputers.count0 == 2L);

       with (lobjComputers[2L]) {

          assert(countAbove == 2L);
          assert(countBelow == 0L);

          assert(first ! is null && first.ID == r"WS1"c);
          assert(previous ! is null && previous.ID == r"WS2"c);
          assert(next is null);
          assert(last ! is null && last.ID == r"WS3"c);

       }

       with (lobjComputers[1L]) {

          assert(countAbove == 1L);
          assert(countBelow == 1L);

          assert(first ! is null && first.ID == r"WS1"c);
          assert(previous ! is null && previous.ID == r"WS1"c);
          assert(next ! is null && next.ID == r"WS3"c);
          assert(last ! is null && last.ID == r"WS3"c);

       }

       with (lobjComputers[0L]) {

          assert(countAbove == 0L);
          assert(countBelow == 2L);

          assert(first ! is null && first.ID == r"WS1"c);
          assert(previous is null);
          assert(next ! is null && next.ID == r"WS2"c);
          assert(last ! is null && last.ID == r"WS3"c);

       }

       lobjComputers[1L].name = r"lenovo"c; 
assert(lobjComputers[1L].name == r"lenovo"c);

       assert(lobjComputers[0L].position1 == 1L);
       assert(lobjComputers[1L].position1 == 2L);
       assert(lobjComputers[2L].position1 == 3L);

       assert(lobjComputers[0L].position0 == 0L);
       assert(lobjComputers[1L].position0 == 1L);
       assert(lobjComputers[2L].position0 == 2L);

       foreach(lobjComputer; lobjComputers) { /// testing access 
to the collection

          assert(lobjComputer.outer.network == r"lab"c); /// 
testing parent class' properties

       }

    }

}
```


More information about the Digitalmars-d-learn mailing list