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