2010-04-24 3 views
2

Désolé mon titre n'est pas génial, c'est mon premier vrai coup de barre pour passer à 100% à OO car j'ai été procédural pendant plus d'années que je me souvienne. J'ai du mal à comprendre si ce que j'essaie de faire est possible. Selon les pensées des gens sur les 2 points suivants, je vais suivre cette voie. Le CMS que je suis en train de mettre en place est petit, mais il se concentre beaucoup sur différents types de contenu. Je pourrais facilement utiliser Drupal avec lequel je suis très à l'aise, mais je veux me donner de bonnes raisons de passer à des modèles de design/OO-PHPPatterns pour les composants CMS dynamiques (Event driven?)

1) J'ai créé une classe de base 'content' Je souhaite pouvoir étendre pour gérer différents types de contenu. La classe de base, par exemple, gère le contenu HTML et les extensions peuvent gérer les sorties XML ou PDF à la place. D'autre part, à un moment donné, je souhaiterais étendre complètement la classe de base pour un projet donné. C'est à dire. Si la classe 'content-v2' étend la classe 'content' pour ce site, tous les appels à cette classe devraient appeler 'content-v2' à la place. Est-ce possible?

Si le code instancie un objet de type 'content' - je veux en fait instancier un de type 'content-v2' ... Je peux voir comment le faire en utilisant l'héritage, mais cela semble impliquer de se référer à la classe explicitement, je ne vois pas comment lier la classe que je veux utiliser dynamiquement. 2) Deuxièmement, la façon dont je construis cela en ce moment est horrible, je ne suis pas content. Cela semble très linéaire, c'est-à-dire obtenir des détails sur la session> obtenir du contenu> construire la navigation> page du thème> publier. Pour ce faire, tous les objets sont appelés 1 par 1, ce qui est très statique. Je voudrais qu'il soit plus dynamique afin que je puisse y ajouter à une date ultérieure (très proche de la première question). Y a-t-il un moyen, à la place de ma classe orchestrator appelant toutes les autres classes 1-by-1, de construire le tout à la fin, au lieu que chacune des autres classes puisse 'écouter' des événements spécifiques, puis au point applicable sauter et faire leur mais? De cette façon, la classe orchestrator n'aurait pas besoin de savoir quelles autres classes étaient requises, et les appellerait 1-by-1. Désolé, si j'ai tout cela tordu dans ma tête. J'essaye de construire ceci donc c'est vraiment flexible.

Répondre

1

Pour votre question sur le travail avec Content et Content-v2 (qui sont des noms horribles, btw) ...

  1. Go En savoir plus sur encapsulation et polymorphism
  2. Je pense que vous avez besoin d'une classe BaseContent qui est un classe abstraite qui n'est jamais consommée autrement qu'en héritant.
  3. De votre classe BaseContent, vous devriez alors avoir une classe HtmlContent, PdfContent, MSWordContent, etc. Et si vous le souhaitez, vous pouvez même les étendre, comme avec HtmlReportContent (étend HtmlContent), MSWord2010Content, etc.
  4. De cette façon, vous pouvez déclarer et stocker des variables de tout type de BaseContent mais lorsque vous les instanciez, vous instanciez-les en tant que votre type spécifique. Lorsque vous faites cela, même si vous avez une méthode Render() implémentée dans votre classe BaseContent, vos classes enfants peuvent choisir d'utiliser la méthode Render() de base ou la remplacer et fournir leur propre implémentation, ou même la remplacer, fournir un peu implémentation personnalisée, puis appelez l'implémentation de base pour bénéficier également de cette implémentation.

Si vous avez mise en œuvre partagée ou d'un comportement (par exemple, un Chihuahua et une classe GermanShepard aurait à la fois une fonction Bark() mais ils seraient mis en œuvre différemment), puis abstraite qui partage la mise en œuvre ou d'un comportement à une classe de base. Vous n'avez pas nécessairement à fournir une implémentation dans votre classe de base - c'est pourquoi c'est une classe abstraite - cela force vos classes enfants à l'implémenter (Bark() serait défini dans votre classe BaseDog mais pas implémenté). Pour le flux de votre système ... Pour votre conception orientée objet, pensez à concevoir un state machine et vos objets remplissent essentiellement le but de cette machine d'état et chaque changement d'un état à un autre. Oui, il y a probablement une façon de parcourir votre diagramme de flux qui couvre 50% des scénarios, mais nous savons tous que les utilisateurs vont s'en écarter. Vous devez donc définir les façons dont un utilisateur peut s'en écarter et autoriser celles-ci, mais restreindre celles-ci. Vous pouvez ou non dessiner un organigramme, selon la complexité de votre système. Mais parfois, il aide à visualiser certaines des possibilités en en tirant une partie. Généralement, cependant, ce diagramme serait trop important pour la plupart des systèmes OO. Si vous faisiez un système pour la NASA, vous l'auriez probablement, cependant. :-P

Voici un code pour illustrer certaines de ces choses (et pour répondre à certaines de vos questions dans les commentaires). Cependant, je ne suis pas un mec PHP donc je vais vous donner des exemples en C# mais je vais les garder assez simples pour que vous puissiez traduire en PHP assez facilement.

public interface IAnimal 
{ 
    string GetName(); 
    string Talk(); 
} 

public abstract class AnimalBase : IAnimal 
{ 
    private string _name; 

    // Constructor #1 
    protected AnimalBase(string name) 
    { 
     _name = name; 
    } 

    // Constructor #2 
    protected AnimalBase(string name, bool isCutsey) 
    { 
     if (isCutsey) 
     { 
      // Change "Fluffy" into "Fluffy-poo" 
      _name = name + "-poo"; 
     } 
    } 

    // GetName implemention from IAnimal. 
    // In C#, "virtual" means "Let the child class override this if it wants to but is not required to" 
    public virtual string GetName() 
    { 
     return _name; 
    } 

    // Talk "implementation" from IAnimal. 
    // In C#, "abstract" means "require our child classes to override this and provide the implementation". 
    // Since our base class forces child classes to provide the implementation, this takes care of the IAnimal implementation requirement. 
    abstract public string Talk(); 
} 

public class Dog : AnimalBase 
{ 
    // This constructor simply passes on the name parameter to the base class's constructor. 
    public Dog(string name) 
     : base(name) 
    { 
    } 

    // This constructor passes on both parameters to the base class's constructor. 
    public Dog(string name, bool isCutsey) 
     : base(name, isCutsey) 
    { 
    } 

    // Override the base class's Talk() function here, and this satisfy's AnimalBase's requirement to provide this implementation for IAnimal. 
    public override string Talk() 
    { 
     return "Woof! Woof!"; 
    } 
} 

public class SmallDog : Dog 
{ 
    private bool _isPurseDog; 

    // This constructor is unique from all of the other constructors. 
    // Rather than the second boolean representing the "isCutsey" property, it's entirely different. 
    // It's entirely a coincidence that they're the same datatype - this is not important. 
    // Notice that we're saying ALL SmallDogs are cutsey by passing a hardcoded true into the base class's (Dog) second parameter of the constructor. 
    public SmallDog(string name, bool isPurseDog) 
     : base(name, true) 
    { 
     _isPurseDog = isPurseDog; 
    } 

    // This tells us if the dog fits in a purse. 
    public bool DoesThisDogFitInAPurse() 
    { 
     return _isPurseDog; 
    } 

    // Rather than using Dog's Talk() implementation, we're changing this because this special type of dog is different. 
    public override string Talk() 
    { 
     return "Yip! Yip!"; 
    } 
} 

public class Chihuahua : SmallDog 
{ 
    private int _hatSize; 

    // We say that Chihuahua's always fit in a purse. Nothing else different about them, though. 
    public Chihuahua(string name, int hatSize) 
     : base(name, true) 
    { 
     _hatSize = hatSize; 
    } 

    // Of course all chihuahuas wear Mexican hats, so let's make sure we know its hat size! 
    public int GetHatSize() 
    { 
     return _hatSize; 
    } 
} 

public class Cat : AnimalBase 
{ 
    // This constructor simply passes on the name parameter to the base class's constructor. 
    public Cat(string name) 
     : base(name) 
    { 
    } 

    // This constructor passes on both parameters to the base class's constructor. 
    public Cat(string name, bool isCutsey) 
     : base(name, isCutsey) 
    { 
    } 

    // Override the base class's Talk() function here, and this satisfy's AnimalBase's requirement to provide this implementation for IAnimal. 
    public override string Talk() 
    { 
     return "Meoooowwww..."; 
    } 
} 

public class Lion : Cat 
{ 
    public Lion(string name) 
     : base(name) 
    { 
    } 

    // Rather than using Cat's Talk() implementation, we're changing this because this special type of cat is different. 
    public override string Talk() 
    { 
     return "ROAR!!!!!!!!"; 
    } 
} 

public class ThisIsNotAGoodExampleOfObjectOrientedCoding 
{ 
    public string DoStuff() 
    { 
     // To keep the C#-to-PHP translation easy, think of this as an array of IAnimal objects. 
     List<IAnimal> myAnimals = new List<IAnimal>(); 

     IAnimal strayCat = new Cat("Garfield", false); 
     Cat myPet = new Cat("Katrina"); 
     IAnimal myMothersDog = new Dog("Harley"); 
     Dog myMothersOtherDog = new Dog("Cotton"); 
     IAnimal myNeighborsDog = new SmallDog("Roxy", false); 
     Dog movieStarsDog = new SmallDog("Princess", true); 
     Dog tacoBellDog = new Chihuahua("Larry", 7); 
     Lion lionKing = new Lion("Simba"); 

     myAnimals.Add(strayCat); 
     myAnimals.Add(myPet); 
     myAnimals.Add(myMothersDog); 
     myAnimals.Add(myMothersOtherDog); 
     myAnimals.Add(myNeighborsDog); 
     myAnimals.Add(movieStarsDog); 
     myAnimals.Add(tacoBellDog); 
     myAnimals.Add(lionKing); 

     string allAnimalsTalking = ""; 

     // Create a string to return. 
     // Garfield says "Meow". Fido says "Woof! Woof!" etc... 
     for (int i = 0; i < myAnimals.Count; i++) 
     { 
      allAnimalsTalking = allAnimalsTalking + myAnimals[i].GetName() + " says \"" + myAnimals[i].Talk() + "\" "; 

      if (myAnimals[i] is SmallDog) 
      { 
       // Cast the IAnimal into a SmallDog object. 
       SmallDog yippyDog = myAnimals[i] as SmallDog; 

       if (yippyDog.DoesThisDogFitInAPurse()) 
       { 
        allAnimalsTalking = allAnimalsTalking + " from a purse."; 
       } 
      } 
     } 

     return allAnimalsTalking; 
    } 
} 

J'espère que cela aide certains.

+0

Bien que daté, son apparence comme une bonne ressource (PHP OO eBook gratuit) pour commencer avec quelques-unes des bases: http://www.pdf-word.net/Tutorial-Programming-PHP/Object-Oriented-Programming -In-PHP5.html – Jaxidian

+0

Merci beaucoup pour votre réponse brillante, très appréciée! Je pensais avoir compris le polymorphisme, mais clairement non parce que je ne croyais pas que c'était une solution ... Ps ces noms de classe n'étaient pas les vrais, juste une tentative pour illustrer ma question: 0) – CitrusTree

+0

Donc ce qui me trouble est illustré ici: http://en.wikipedia.org/wiki/Polymorphism_in_object-oriented_programming#PHP Je comprends la chose Chien/Chien, mais Cat et Dog sont toujours mentionnés explicitement pour les instituer. Comment appelons-nous Cat ou Dog en fonction de l'état actuel. Disons que chaque enregistrement de la table de contenu a enregistré son type de contenu. Pourriez-vous utiliser quelque chose comme: $ content = new $ row ['type']; $ contenu-> publier; ... où $ row est la ligne courante du jeu d'enregistrements et le type est un nom de classe (valide)? – CitrusTree