D modeling

CheeseWiz CheesyFritos at gmail.com
Fri Jul 5 11:22:10 UTC 2019


On Thursday, 4 July 2019 at 04:33:12 UTC, Jesse Phillips wrote:
> On Wednesday, 3 July 2019 at 19:31:49 UTC, Bert wrote:
>>
>> The problem with removing the interfaces is that when you try 
>> to extend the model by adding new classes you will then have 
>> multiple inheritance of classes and be screwed. You may be 
>> able to remove them in the simple example I gave but in 
>> general it will be impossible to make it work. One of the main 
>> reasons for interfaces is precisely to allow MI in a single 
>> inheritance language. The interfaces are the only way to avoid 
>> such future problems. In essence you are creating an 
>> anti-pattern.
>
> Huh, not sure what is up with that:
> https://gist.github.com/run-dlang/bfc043093066da04f0d2bec5a07de459
>

That works.

> I understand the purpose for interfaces, what I'm trying to 
> understand is your requirements for this 'model' idea.
>
> Your code made overzealous use of inheritance which I expect 
> created some of your frustration. When I stated I did not see 
> multiple inheritance in your code, you pursued it was and the 
> interfaces were required. I have shown this is not true, so I 
> ask you to consider what your needs actually are. I'm not 
> creating any patterns I'm informing you of how the language 
> works and meets your demonstrated need.


You seem to think the example is a real world model and that all 
other models would work the same.

Let me ask you, do you think interfaces are never needed in oop? 
If they are, why? Do you realize that the modeling is just oop?

Now, I will explain why you are wrong, misunderstanding the 
problem, and how you changed the entire meaning of the program to 
cram it in to your misunderstanding.

1.

struct ModelB
{	
     class Animal : ModelA.Animal
     {

     }

     class Cat : ModelA.Cat
     {
		override Food LikesWhichFood() { writeln("Food D Type: ", 
fullyQualifiedName!Food); return new Cabbage; }					
		this() { }
		this(string n) { name = n; }
		
     }

	class Dog : ModelA.Dog
     {
		override Food LikesWhichFood() { writeln("Food D Type: ", 
fullyQualifiedName!Food); return new Donuts; }					
		this() { }
		this(string n) { name = n; }
     }

You've removed all the interfaces so you would not have MI. You 
believe that was as smart thing. But you have completely 
disconnected  ModelB from itself.

Now ModelB.Dog does not inheret from ModelB.Animal.

What happens if you extend ModelB.Animal? How is ModelB.Dog going 
to see that? It can't, it's not connected. You have not created a 
new model.  You have simply extended every class in ModelA. You 
have done nothing model wise.

2.

My original code:

	ModelA.iAnimal animal1 = new ModelB.Cat("Super Mittens");
	ModelA.iAnimal animal2 = new ModelB.Dog("Super Sparky");

you changed the returns to auto.

In mine, I'm using oop and animal1 is of class type ModelA but of 
instance type ModelB. By using auto you still have ModelB's(even 
though they are not longer really a Model). This may seem like no 
problem in this simple case but realize in a real problem ModelA 
be creating new objects using a factory. You won't have access to 
changing everything so easily. The whole point I did not use auto 
was to express this. I wanted to show how how animal1 as a ModelA 
could hold a ModelB and act properly.(i.e., inheritance).

If one has a class hierarchy, this is what you have done:

    A
  /   \
B     C

and you you have

    A  ---->  a
   /    \
B -> b   C -> c

Every class is just has a new derived base class for you to 
insert. This is just basic oop. This is not on a higher level. c 
is not related to a as C is to A.

What I have is this

   A  ---------->  a
/   \           /   \
B   C          b    c
-------------->
     --------------->


compared to yours:

   A  ---------->  a
/   \
B   C          b    c
-------------->
     --------------->


(notice that a, b, and c are not related directly in the fashion 
of the structure).

3. You have to realize you are wrong, that is the first step to 
understanding. I've tried to point it out but you are probably 
still thinking "But I'm right and he's wrong".

What you are doing is not modeling. That is the first 
realization. You have done nothing outside basic oop that 
everyone does. It's just class inheritence. You have some class X 
and you want a new one so you derive from it. You have a class Y 
and you want some new Y and you derive from it... but your does 
not explicitly connect X and Y in any way shape or form if they 
are connected.


What I'm doing is preserving the structure. When we derive a 
class, we preserve the base classes structure. This is called an 
embedding. The base class is embedded in the derived class and 
this allows the derived class to be the base class. That is, we 
can always substitute the derived in places where the base class 
is because the derived is the base class in *structure*.

But the same logic holds for relations between classes(which are 
models, frameworks, designs, etc). I'm trying to preserve the 
structure of the classes AND the structure between the classes. 
Do you see this?

https://en.wikipedia.org/wiki/Natural_transformation


The only thing I can say is that with what you are doing, you 
cannot subsistent one model for the other because you actually 
only have one model.

In my original code I have two models. The second model is fully 
derived from the first. It contains the entire structure of the 
first. It is true inheritance of models and it is oop.

It means that if a program was written with ModelA in mind, I can 
derive a new model called ModelB and drop it in place of it. I 
can extend my model and inside the model everything is in terms 
of ModelB(not mixing modelA stuff).


Look at this, the lines are inheritance:

   A  ---------->  a
/   \           /   \
B   C          b    c
-------------->
     --------------->


compared to yours:

   A  ---------->  a
/   \
B   C          b    c
-------------->
     --------------->


In yours, if I create a class b, it contains B and then A, but no 
a.
In mine, b contains B, A, AND a.

Yours you cannot get a. How will you get it's members? As far as 
your model is concerned, a is not connected to b. In mine a is 
directly connected to be(it inherits it). And the relationships B 
: A is preserved b : a.

You can see this in the code by trying to add a new method to 
ModelB.Animal... it does not show up in ModelB.Dog... 
ModelB.Animal is not related to ModelB.Dog. Hence you are not 
preserving the structure in ModelB that exists in ModelA.

This means if someone comes along and decides to model ModelB, 
they will be screwed because it no longer models A. You have 
broken "model inheritance".

Class inheritence doesn't break like this because it preserves 
the internal structure. Model inheritance doesn't break this 
because it preserves the internal structure... but D doesn't know 
about model inheritance so one can actually break it.


It's only going to be clear when you realize there is more to it 
than what you are thinking about. It may or may not be subtle.

It's purpose is a little more practical though. Your method will 
work but it requires hacks, factors, and all kinds of things to 
keep the modeling consistent and useful. You can cast objects and 
use factors to generate the appropriate types...

But in what I'm doing, one does not have too do all that.

ModelB knows about itself. ModelB.Dog knows about 
ModelB.Animal... not only ModelA.Animal.

Do you see the difference? I'm adding an arrow, it's not just an 
arrow, it's a structural relationship that has meaning(it is 
inheritance). I'm using *multiple inheritance* and you are using 
single inheritance. You can claim that I don't need multiple 
inheritance but really, you should be skeptical since obviously 
multiple inheritance will generally provide more(but never less).

You are throwing out a connection to fit the problem in to your 
own misconception.

If you want to see your problem, it's quite simple:


struct ModelB
{	
     class Animal : ModelA.Animal
     {
        void Fart();
     }

}

Your Dog is oblivious to Fart.. It can't see it, there is no 
arrow. Add it to your code...  You cannot do dog.Fart. You can 
cast, you can play games, but it is not naturally done like it 
should(like standard inheritance should work).

But if you do what I do, or had MI:

     class Dog : Animal, ModelA.Animal

Now Dog can see Fart. Now that is MI, can't be done in D, so we 
have to use interfaces.

Do you get it? The main thing to recognize is that you are 
pruning the structure... and so you have to absolutely make sure 
you are not cutting the tree down in the process, and 
unfortunately that is what you did in this case. You just need to 
recognize there is more too it to start seeing what it is. As 
long as you keep believing it can be simplified you will continue 
going down that dead end.





More information about the Digitalmars-d mailing list