Staram się przestrzegać zasady DRY w moim programowaniu tak mocno, jak potrafię. Ostatnio uczyłem się wzorców projektowych w OOP i skończyło się na tym, że powtarzałem sobie całkiem sporo.
Utworzyłem wzorzec repozytorium wraz ze wzorami Factory i Gateway, aby obsłużyć moją trwałość. Korzystam z bazy danych w mojej aplikacji, ale to nie powinno mieć znaczenia, ponieważ powinienem móc wymienić bramkę i przejść na inny rodzaj uporczywości, jeśli chciałbym.
Problem, który stworzyłem dla siebie, polega na tym, że tworzę te same obiekty dla liczby posiadanych tabel. Na przykład będą to obiekty, których potrzebuję do obsługi tabeli 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);
}
}
Wtedy wygląda mój kontroler
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));
}
}
Pomyślałem więc, że prawidłowo używam wzorców projektowych i przestrzegam dobrych praktyk, ale problem polega na tym, że kiedy dodam nową tabelę, muszę stworzyć te same klasy tylko z innymi nazwami. Rodzi to we mnie podejrzenie, że mogę robić coś złego.
Pomyślałem o rozwiązaniu, w którym zamiast interfejsów miałem abstrakcyjne klasy, które używając nazwy klasy określają tabelę, którą muszą manipulować, ale to nie wydaje się właściwe, co zrobić, jeśli zdecyduję się na przechowywanie plików lub memcache, gdzie nie ma tabel.
Czy podchodzę do tego poprawnie, czy jest inna perspektywa, na którą powinienem patrzeć?