Skip to content

Instantly share code, notes, and snippets.

@JesseKPhillips
Forked from run-dlang/main.d
Last active July 7, 2019 16:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JesseKPhillips/b891604444f4aee72c705b498a206d50 to your computer and use it in GitHub Desktop.
Save JesseKPhillips/b891604444f4aee72c705b498a206d50 to your computer and use it in GitHub Desktop.
Code shared from run.dlang.io.
import std.stdio, std.traits;
struct ModelA
{
// D only allows single inheritance, must use interfaces
interface iAnimal
{
string Type();
string Name();
void Attack(iAnimal who);
iFood LikesWhichFood();
}
interface iCat : iAnimal
{
void Meow();
}
interface iDog : iAnimal
{
void Bark();
}
interface iFood
{
}
class Animal : iAnimal
{
void Attack(iAnimal who) { writeln(Name, " is attacking ", who.Name, "!"); }
string Type() { return "Unknown Animal Type"; }
override string Name() { return "Unknown Animal"; }
iFood LikesWhichFood() { writeln("Food D Type: ", fullyQualifiedName!iFood); return null; }
}
class Cat : Animal, iCat
{
string name = "Unknown Cat";
override string Type() { return "Cat"; }
override string Name() { return name; }
void Meow() { writeln("Meow!"); }
this() { }
this(string n) { name = n; }
}
class Dog : Animal, iDog
{
string name = "Unknown Dog";
override string Type() { return "Dog"; }
override string Name() { return name; }
void Bark() { writeln("Bark!"); }
this() { }
this(string n) { name = n; }
}
class Food : iFood
{
}
}
// Model B, It is "derived" from A, meaning Model B could, in theory, substitute for Model A as long as everything is designed correctly
// In this case we will create a ViewModel, a gui framework for ModelA. We actually cannot do this naturally in D since it does not support multiple inheritance.
struct ModelB
{
interface iAnimal : ModelA.iAnimal
{
// Bring in covarience
override iFood LikesWhichFood();
}
interface iCat : iAnimal
{
}
interface iDog : iAnimal
{
}
interface iFood : ModelA.iFood
{
void IsItTasty();
}
// This isn't even part of the inheritance tree
// This is what Multiple Inheritance would provide
// and cannot be replicated by interfaces
//class Animal : ModelA.Animal, iAnimal
//{
//}
class Cat : ModelA.Cat, iCat // We need to derive from Animal, not iAnimal, to provide proper ModelB implementation of Animal
{
override iFood LikesWhichFood() { writeln("Food D Type: ", fullyQualifiedName!iFood); return new Cabbage; }
this() { }
this(string n) { name = n; }
}
class Dog : ModelA.Dog, iDog
{
override iFood LikesWhichFood() { writeln("Food D Type: ", fullyQualifiedName!iFood); return new Donuts; }
this() { }
this(string n) { name = n; }
}
class Food : iFood
{
void IsItTasty() { assert(false, "Know your Food"); }
}
class Donuts : Food
{
override void IsItTasty() { writeln("YUK!"); }
}
class Cabbage : Food
{
override void IsItTasty() { writeln("YUM!"); }
}
}
void main()
{
{
ModelA.iAnimal animal1 = new ModelA.Cat("Mittens");
ModelA.iAnimal animal2 = new ModelA.Dog("Sparky");
writeln(animal1.Name);
assert(animal1.Name == "Mittens");
writeln(animal2.Name);
assert(animal2.Name == "Sparky");
animal1.Attack(animal2);
animal1.LikesWhichFood;
assert(isImplicitlyConvertible!(typeof(animal1.LikesWhichFood()), ModelA.iFood));
assert(isCovariantWith!(typeof(animal1.LikesWhichFood), typeof(ModelA.Cat.LikesWhichFood)));
assert(isCovariantWith!(typeof(animal2.LikesWhichFood), typeof(ModelA.Dog.LikesWhichFood)));
}
writeln("\n----------\n");
{
ModelB.iAnimal animal1 = new ModelB.Cat("Super Mittens");
ModelB.iAnimal animal2 = new ModelB.Dog("Super Sparky");
writeln(animal1.Name);
assert(animal1.Name == "Super Mittens");
writeln(animal2.Name);
assert(animal2.Name == "Super Sparky");
animal1.Attack(animal2);
auto f = animal1.LikesWhichFood;
f.IsItTasty;
//assert(!__traits(compiles, f.IsItTasty), "is Tasty Compiles"); // Error: no property `IsItTasty` for type `Models.ModelA.iFood`. It should return a ModelB.iFood, we are inside ModelB, never any risk
(cast(ModelB.iFood)f).IsItTasty; // We can, of course, force it, but that is the rub, we don't have to, that is why we want to have a concept of a model, it tells the compiler that there is something more going on and it can reduce all this overhead. We can't even override this because of the contravariance rule.
assert(isImplicitlyConvertible!(typeof(animal1.LikesWhichFood()), ModelA.iFood));
assert(isCovariantWith!(typeof(animal1.LikesWhichFood), typeof(ModelA.Cat.LikesWhichFood)));
assert(isCovariantWith!(typeof(animal2.LikesWhichFood), typeof(ModelA.Dog.LikesWhichFood)));
}
writeln("\n----------\n");
// This is the magic, ModelB is now substituted in Model A. It's basically still oop but our entire derived model is(or should be) used.
// We can substitute the new model in all places where the old was used. This is the easy way to do ModelViewModel, we simply extend the model and add the view, no complex bridging, adapting, maintance, dependencies, etc.
{
ModelA.iAnimal animal1 = new ModelB.Cat("Super Mittens");
ModelA.iAnimal animal2 = new ModelB.Dog("Super Sparky");
writeln(animal1.Name);
assert(animal1.Name == "Super Mittens");
writeln(animal2.Name);
assert(animal2.Name == "Super Sparky");
animal1.Attack(animal2);
animal1.LikesWhichFood;
auto f = animal2.LikesWhichFood;
assert(!__traits(compiles, f.IsItTasty), "is Tasty Compiles"); // This Error is ok, we are inside ModelA, ModelA would never use IsItTasty and it would be wrong to do so(it's only wrong because it should be impossible for ModelA to know about ModelB, else we create a dependency between models and really end up with one combined model rather than two separate models). But note that we could cast
(cast(ModelB.iFood)f).IsItTasty; // We can, of course, force it though(only because we know for a fact we are actually dealing with a ModelB disugised as a ModelA, this is generally not the case), but this then shows a dependency. Note that it is exactly like the above model though... but there is a huge difference. In the first case it is afe, in this case it is not.. and the only difference is the model we are working in.
assert(isImplicitlyConvertible!(typeof(animal1.LikesWhichFood()), ModelA.iFood));
assert(isCovariantWith!(typeof(animal1.LikesWhichFood), typeof(ModelA.Cat.LikesWhichFood)));
assert(isCovariantWith!(typeof(animal2.LikesWhichFood), typeof(ModelA.Dog.LikesWhichFood)));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment