J'essaie de suivre le principe DRY dans ma programmation aussi fort que possible. Récemment, j'ai appris des modèles de conception en POO et j'ai fini par me répéter beaucoup.
J'ai créé un modèle de référentiel avec des modèles d'usine et de passerelle pour gérer ma persistance. J'utilise une base de données dans mon application, mais cela ne devrait pas avoir d'importance car je devrais pouvoir échanger la passerelle et passer à un autre type de persistance si je le souhaitais.
Le problème que j'ai fini par me créer est que je crée les mêmes objets pour le nombre de tables que j'ai. Par exemple, ce seront les objets dont j'ai besoin pour gérer une table comments
.
class Comment extends Model {
protected $id;
protected $author;
protected $text;
protected $date;
}
class CommentFactory implements iFactory {
public function createFrom(array $data) {
return new Comment($data);
}
}
class CommentGateway implements iGateway {
protected $db;
public function __construct(\Database $db) {
$this->db = $db;
}
public function persist($data) {
if(isset($data['id'])) {
$sql = 'UPDATE comments SET author = ?, text = ?, date = ? WHERE id = ?';
$this->db->prepare($sql)->execute($data['author'], $data['text'], $data['date'], $data['id']);
} else {
$sql = 'INSERT INTO comments (author, text, date) VALUES (?, ?, ?)';
$this->db->prepare($sql)->execute($data['author'], $data['text'], $data['date']);
}
}
public function retrieve($id) {
$sql = 'SELECT * FROM comments WHERE id = ?';
return $this->db->prepare($sql)->execute($id)->fetch();
}
public function delete($id) {
$sql = 'DELETE FROM comments WHERE id = ?';
return $this->db->prepare($sql)->execute($id)->fetch();
}
}
class CommentRepository {
protected $gateway;
protected $factory;
public function __construct(iFactory $f, iGateway $g) {
$this->gateway = $g;
$this->factory = $f;
}
public function get($id) {
$data = $this->gateway->retrieve($id);
return $this->factory->createFrom($data);
}
public function add(Comment $comment) {
$data = $comment->toArray();
return $this->gateway->persist($data);
}
}
Ensuite, mon contrôleur ressemble
class Comment {
public function view($id) {
$gateway = new CommentGateway(Database::connection());
$factory = new CommentFactory();
$repo = new CommentRepository($factory, $gateway);
return Response::view('comment/view', $repo->get($id));
}
}
J'ai donc pensé utiliser correctement les modèles de conception et conserver les bonnes pratiques, mais le problème avec cette chose est que lorsque j'ajoute une nouvelle table, je dois créer les mêmes classes uniquement avec d'autres noms. Cela me fait soupçonner que je fais peut-être quelque chose de mal.
J'ai pensé à une solution où, au lieu d'interfaces, j'avais des classes abstraites qui, à l'aide du nom de classe, définissaient le tableau qu'elles devaient manipuler, mais cela ne semblait pas être la bonne chose à faire, si je décidais de passer à un stockage de fichiers ou memcache où il n'y a pas de tables.
Suis-je en train de l'aborder correctement ou y a-t-il une perspective différente que je devrais envisager?
la source
Réponses:
Le problème que vous abordez est assez fondamental.
J'ai rencontré le même problème lorsque j'ai travaillé pour une entreprise qui créait une grande application J2EE composée de plusieurs centaines de pages Web et de plus d'un million et demi de lignes de code Java. Ce code utilisait ORM (JPA) pour la persistance.
Ce problème s'aggrave lorsque vous utilisez des technologies tierces dans chaque couche de l'architecture et que les technologies nécessitent toutes leur propre représentation des données.
Votre problème ne peut pas être résolu au niveau du langage de programmation que vous utilisez. L'utilisation de modèles est bonne, mais comme vous le voyez, cela provoque la répétition du code (ou plus exactement: répétition des conceptions).
D'après moi, il n'y a que 3 solutions possibles. En pratique, ces solutions se résument au même.
Solution 1: utilisez un autre cadre de persistance qui vous permet d'indiquer uniquement ce qui doit être conservé. Il existe probablement un tel cadre. Le problème avec cette approche est qu'elle est plutôt naïve car tous les modèles ne seront pas liés à la persistance. Vous voudrez également utiliser des modèles pour le code d'interface utilisateur, vous aurez donc besoin d'un cadre d'interface graphique qui peut réutiliser les représentations de données du cadre de persistance que vous choisissez. Si vous ne pouvez pas les réutiliser, vous devrez écrire du code de plaque de chaudière pour relier les représentations de données du framework GUI et du framework de persistance .. et cela est à nouveau contraire au principe DRY.
Solution 2: utilisez un autre langage de programmation - plus puissant - qui a des constructions qui vous permettent d'exprimer la conception répétitive afin que vous puissiez réutiliser le code de conception. Ce n'est probablement pas une option pour vous, mais supposez-le un instant. Ensuite, lorsque vous commencez à créer une interface utilisateur au-dessus de la couche de persistance, vous souhaiterez à nouveau que le langage soit suffisamment puissant pour prendre en charge la création de l'interface graphique sans avoir à écrire le code de la plaque de la chaudière. Il est peu probable qu'il existe un langage suffisamment puissant pour faire ce que vous voulez, car la plupart des langues s'appuient sur des cadres tiers pour la construction de l'interface graphique qui nécessitent chacun leur propre représentation des données pour fonctionner.
Solution 3: automatisez la répétition du code et de la conception à l'aide d'une forme de génération de code. Votre souci est de devoir coder à la main les répétitions de motifs et de dessins car le code / dessin répétitif à codage manuel viole le principe DRY. De nos jours, il existe des frameworks de génération de code très puissants. Il existe même des «ateliers de langage» qui vous permettent de créer rapidement (une demi-journée sans expérience) votre propre langage de programmation et de générer n'importe quel code (PHP / Java / SQL - n'importe quel fichier texte imaginable) à l'aide de ce langage. J'ai de l'expérience avec XText mais MetaEdit et MPS semblent bien aussi. Je vous conseille fortement de vérifier l'un de ces ateliers linguistiques. Ce fut pour moi l'expérience la plus libératrice de ma vie professionnelle.
En utilisant Xtext, votre machine peut générer le code répétitif. Xtext génère même pour vous un éditeur de coloration syntaxique avec complétion de code pour votre propre spécification de langage. À partir de là, il vous suffit de prendre votre passerelle et votre classe d'usine et de les transformer en modèles de code en y perforant des trous. Vous les alimentez dans votre générateur (qui est appelé par un analyseur de votre langue qui est également complètement généré par Xtext) et le générateur remplira les trous dans vos modèles. Le résultat est un code généré. À partir de là, vous pouvez supprimer n'importe quelle répétition de code n'importe où (code de persistance de code GUI, etc.).
la source
Le problème auquel vous êtes confronté est ancien: le code des objets persistants ressemble souvent à chaque classe, il s'agit simplement de code passe-partout. C'est pourquoi certaines personnes intelligentes ont inventé les mappeurs relationnels d'objets - ils résolvent exactement ce problème. Voir cet ancien article SO pour une liste des ORM pour PHP.
Lorsque les ORM existants ne subissent pas vos besoins, il existe également une alternative: vous pouvez écrire votre propre générateur de code, qui prend une méta description de vos objets pour persister et génère la partie répétitive du code à partir de cela. Ce n'est en fait pas trop difficile, je l'ai fait dans le passé pour certains langages de programmation différents, je suis sûr qu'il sera également possible d'implémenter de telles choses également en PHP.
la source
Model::getByPK
méthode et dans l'exemple ci-dessus, je serais capable de le faire,Comment::getByPK
mais obtenir les données de la base de données et construire l'objet est tout contenu dans la classe d'objet de données, ce qui est le problème que j'essaie de résoudre en utilisant des modèles de conception .