Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Architekt

Community-Fossil

  • »Architekt« ist der Autor dieses Themas

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

11

04.02.2017, 14:34

Das Design war mir vorgegeben, aber es gab wohl folgende Gründe: Die Conditions sollen geschachtelt & miteinander kombiniert werden können. Z.b. new AndCondition(new HasAge(18), new HasSiblings(2))->fulfills($creature) wobei $creature sowohl Animal oder Human sein kann. Diese And, Or etc. Conditions sollen für alle Creatures funktionieren. Die konkreten Bedingungen sollen dann jedoch spezifisch sein, so das HasAge bzw. HasSiblings nur Human akzeptieren. Es soll aber natürlich trotzdem sowas funktionieren new AndCondition(new HasAge(18), new OrCondition(new HasSiblings(2), new HasCar())->fulfills($creature) deswegen brauchen alle Conditions ein gemeinsames interface. Aber in den spezifischen Conditions muss dann auf bestimmte Eigenschaften zugegriffen werden können (wie Geburtstag, Anzahl der Geschwister, Anzahl der Autos) die nur Humans oder Animals haben aber (meistens) nicht beide. Dazu muss dann ermittelt werden können, ob es ein Human oder ein Animal ist. Und die packages sollen nichts voneinander Wissen (wenn möglich) sonst würde ich einfach einen Visitor benutzen den das CreatureInterface akzeptiert und gut ist.
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

12

04.02.2017, 16:51

Ich verstehe nicht warum die Conditions nur für eine bestimmte Klasse eine Methodendeklaration haben sollten. Mit dem von Dir beschriebenen Konstrukt hat das ja nichts zu tun. Auch warum deine Creatures selbst eine Methode haben, mit der sie sich selbst auf eine Bedingung prüfen können verstehe ich nicht.

Sowas vielleicht?

Quellcode

1
2
3
4
5
6
7
8
9
10
abstract class HumanCondition implements CreatureCondition {
  public final function fulfills( Creature $creature ):bool {
     if( $creature instanceof Human ) {
       return fulfillsHuman($creature);
     }
     return false; // oder Exception werfen
  }

  public abstract function fulfillsHuman(Human $human): bool;
}


Beim Schachteln der Conditions geht der spezifische Typ der Condition ja eh verloren. Die Conditions sollten also selbst wissen, ob sie auf einen bestimmten Typ anwendbar sind. Man kann ja ohne Probleme mit And eine Animal- und eine HumanCondition zusammensetzen, die per se nie erfüllbar ist.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Chromanoid« (04.02.2017, 16:58)


Architekt

Community-Fossil

  • »Architekt« ist der Autor dieses Themas

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

13

04.02.2017, 17:31

Ich verstehe nicht warum die Conditions nur für eine bestimmte Klasse eine Methodendeklaration haben sollten.
Weil z.B. Animals keine Autos haben. Deswegen braucht man den Typ. Wenn du Vorschläge hast bin ich völlig offen und präsentiere sie gern am Montag. Mich nervt das ganze auch leicht und ich hätte gern was "anständiges".


Mit dem von Dir beschriebenen Konstrukt hat das ja nichts zu tun. Auch warum deine Creatures selbst eine Methode haben, mit der sie sich selbst auf eine Bedingung prüfen können verstehe ich nicht.


Wahrscheinlich damit man $condition->fulfills($creature); und $create->fulfills($condition); schreiben kann? Ich weiß es auch nicht so genau.


Sowas vielleicht?

Quellcode

1
2
3
4
5
6
7
8
9
10
abstract class HumanCondition implements CreatureCondition {
  public final function fulfills( Creature $creature ):bool {
     if( $creature instanceof Human ) {
       return fulfillsHuman($creature);
     }
     return false; // oder Exception werfen
  }

  public abstract function fulfillsHuman(Human $human): bool;
}


Beim Schachteln der Conditions geht der spezifische Typ der Condition ja eh verloren. Die Conditions sollten also selbst wissen, ob sie auf einen bestimmten Typ anwendbar sind. Man kann ja ohne Probleme mit And eine Animal- und eine HumanCondition zusammensetzen, die per se nie erfüllbar ist.


Ja, instanceof wäre eine Idee bin aber kein Fan davon eig. :/ Ich hatte gehofft, dass man das irgendwie schön lesen kann.
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

14

04.02.2017, 17:55

Naja, instanceof ist für Bedingungen, die den Typ prüfen sollen, ja praktisch unerlässlich. Wie würdest Du das in C++ lösen? Da würde man doch auch ein Typ-Feld abfragen oder nicht?

Also ich glaube Du solltest es wie folgt machen:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
interface Creature
{
    public function fulfills(CreatureCondition $condition): bool;
}

interface CreatureCondition
{
    public function fulfillsCreature(Creature $creature): bool;
}

abstract class HumanCondition implements CreatureCondition {
  public final function fulfillsCreature( Creature $creature ):bool {
     if( $creature instanceof Human ) {
       return fulfillsHuman($creature);
     }
     return false; // oder Exception werfen
  }

  public abstract function fulfillsHuman(Human $human): bool;
}

abstract class AnimalCondition implements CreatureCondition {
  public final function fulfillsCreature( Creature $creature ):bool {
     if( $creature instanceof Animal) {
       return fulfillsAnimal($creature);
     }
     return false; // oder Exception werfen
  }

  public abstract function fulfillsAnimal(Animal $animal): bool;
}

class Human implements Creature
{
    public function fulfills(CreatureCondition $condition): bool
    {
        return $condition.fulfillsCreature($this);
    }
}

class Animal implements Creature
{
    public function fulfills(CreatureCondition $condition): bool
    {
        return $condition.fulfillsCreature($this);
    }
}

Den Zyklus zwischen Condition und Creature finde ich äußerst unschön. Es ist eine Unverschämtheit, dass das eine Vorgabe ist.

Das mit dem Auto würde man dann so realisieren:

Quellcode

1
2
3
4
5
class HasCar extends HumanCondition {
  public function fulfillsHuman(Human $human): bool {
    return count($human->$cars) > 0;
  }
}


Zu instanceof noch mal: https://sites.google.com/site/steveyegge…ymorphism-fails

Zitat

In the meantime, I hope I've made my main point clear, which is that polymorphism only makes sense when the polymorphic behavior is really a behavior of the target. When it's the behavior of the observer, you need runtime typing.

Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von »Chromanoid« (04.02.2017, 18:15)


BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

15

04.02.2017, 18:24

Mit Interfaces verschleiert man die Zyklen ja einfach nur.
Runtime-Zyklen sind total in Ordnung. Dependency-Zyklen sind problematisch. Interfaces und ein Mediator sind aber absolut sauber.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

16

04.02.2017, 18:42

Ja schon, aber dann muss man bei einer neuen Creature, einer Löschung oder Umbenennung doch auch den Mediator anpassen - das ist sozusagen ein Zyklus zur Entwicklungszeit. Das finde ich sehr unschön. Würde man das in separaten Modulen entwickeln, müsste man immer gleich auch das ConcreteMediator-Modul anpassen, obwohl man eigentlich nur das Modul Plant hinzufügen möchte. Das ist ja der extreme Nachteil vom Mediator - monolithische Strukturen entstehen - es gibt eine Stelle die alle anderen Stellen kennen muss... Mit instanceof kann man das ganze sehr erweiterbar ohne zentrale Stelle lösen - viel flexibler und bei größeren Entwicklungen mit Modulen usw. viel leichter zu handhaben. Die Lösung mit instanceof funktioniert im Kleinen und im Großen gut.

Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von »Chromanoid« (04.02.2017, 19:17)


BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

17

04.02.2017, 20:01

müsste man immer gleich auch das ConcreteMediator-Modul anpassen, obwohl man eigentlich nur das Modul Plant hinzufügen möchte.
Nope. Ich wüsste nicht wieso Hinzufügen irgendeinen Einfluss auf einen bestehenden Mediator hätte. Im Gegenteil, mit Instanceof müsstest du an viel mehr existierende Stellen ran, wenn du eine Klasse entfernst oder hinzufügst. Entfernst du beim Mediator eine der Klassen, löschst du halt einfach nur den entsprechenden Mediator.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

Architekt

Community-Fossil

  • »Architekt« ist der Autor dieses Themas

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

18

04.02.2017, 22:07

Ich bin absolut kein Fan von instanceof. Ich denke ich werd' das ganze folgendermaßen abändern, auch wenn dann Creature mehr oder minder weiß, das es Human und Animal gibt:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?php

interface ConditionInterface
{
    public function fulfillsHuman(Human $human): bool;
    public function fulfillsAnimal(Animal $animal): bool;
}

interface ExpressionInterface
{
    public function fulfills(CreatureInterface $creature): bool;
}

interface CreatureInterface
{
    public function fulfills(ConditionInterface $condition): bool;
}

// ----

abstract class HumanCondition implements ExpressionInterface, ConditionInterface
{
    final public function fulfills(CreatureInterface $creature): bool
    {
        return $creature->fulfills($this);
    }

    final public function fulfillsAnimal(Animal $animal): bool
    {
        throw new Exception('HumanCondition erlaubt keine Animals');
    }
}

final class Human implements CreatureInterface
{
    public function fulfills(ConditionInterface $condition): bool
    {
        return $condition->fulfillsHuman($this);
    }
}


Habt ihr vllt. noch Verbesserungen oder Kritik?
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

19

05.02.2017, 13:12

Das finde ich die schlechteste Lösung, völlig nervig erweiterbar. Bei jeder neuen Creature, muss man alle Klassen anpassen... Aber Du kennst da ja jetzt meinen Standpunkt ^^

Du verpasst echt was, wenn Du instanceof so abtust ;). Mit Typen kann man echt tolle Sachen machen, Ceylon finde ich da auch echt spannend: https://ceylon-lang.org/documentation/1.3/tour/types/

20

05.02.2017, 13:27

müsste man immer gleich auch das ConcreteMediator-Modul anpassen, obwohl man eigentlich nur das Modul Plant hinzufügen möchte.
Nope. Ich wüsste nicht wieso Hinzufügen irgendeinen Einfluss auf einen bestehenden Mediator hätte. Im Gegenteil, mit Instanceof müsstest du an viel mehr existierende Stellen ran, wenn du eine Klasse entfernst oder hinzufügst. Entfernst du beim Mediator eine der Klassen, löschst du halt einfach nur den entsprechenden Mediator.
Das stimmt doch einfach nicht. Mit instanceof muss ich nur die Conditions anfassen, die auch die entsprechende Kreatur betreffen.

Noch mal ein Beispiel in einem etwas größeren Zusammenhang (ich mache mal I für Interface, klein geschrieben sind die Module). Variante mit Laufzeit-Typ-Bestimmung:

Ausgangslage:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
creature
  ICreature
  ICreatureCondition

animal
  IAnimal
  IsBreeding

human
  IHuman
  HasCar


So nun kommen Pflanzen dazu, * für die Stellen, die angepasst werden müssen:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
creature
  ICreature
  ICreatureCondition

animal
  IAnimal
  IsBreeding

human
  IHuman
  HasCar

plant*
  IPlant*
  IsTree*


Nun die Variante mit Mediator:
Ausgangslage:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
creature
  ICreature
  ICreatureCondition
  IMediator

animal
  IAnimal
  IsBreeding

human
  IHuman
  HasCar

concretemediator
  MyMediator

Nun kommen die Pflanzen dazu:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
creature
  ICreature
  ICreatureCondition
  IMediator

animal
  IAnimal
  IsBreeding

human
  IHuman
  HasCar

plant*
  IPlant*
  IsTree*

concretemediator
  MyMediator* <-----


Ich weiß nicht welche Software-Architektur-Bücher ihr so lest, aber die Variante mit instanceof ist meinem Wissensstand nach klar überlegen.

Stellt euch nur mal vor, das ganze wird in mehreren Repositories entwickelt, oder man selbst will eine neue Creature hinzufügen ohne dass man den Code vom Mediator anpassen kann:

concreteMediator
dependsOn human
dependsOn animal
dependsOn plant

application1
dependsOn concreteMediator

customizedApplication1
dependsOn application1
+ fish
+ concreteMediatorWithFish

Man kann die Anwendung dann nicht erweitern ohne auch einen eigenen Mediator zu injecten. Ziemlich kacke. Würde man das ganze noch dynamisch Linken wollen und neue Kreaturen wie eine Art Plugin dazuladen wollen, müsste man eine Registratur für Mediatoren haben, die dann alle abgefragt werden müssten. Dann müsste ein delegierender Mediator an alle Stellen injected werden, die IMediator verwenden. Völlig unnötige Kopplung...

Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von »Chromanoid« (05.02.2017, 13:39)


Werbeanzeige