Prinsip KERING dalam praktik yang baik?

11

Saya mencoba mengikuti prinsip KERING dalam pemrograman saya sekuat yang saya bisa. Baru-baru ini saya telah belajar pola desain di OOP dan akhirnya saya mengulangi cukup banyak.

Saya telah membuat pola Repositori bersama dengan pola Pabrik dan Gateway untuk menangani kegigihan saya. Saya menggunakan database dalam aplikasi saya tapi itu tidak masalah karena saya harus bisa menukar Gateway dan beralih ke jenis kegigihan lain jika saya mau.

Masalah yang akhirnya saya buat sendiri adalah bahwa saya membuat objek yang sama untuk jumlah tabel yang saya miliki. Misalnya ini akan menjadi objek yang saya butuhkan untuk menangani tabel 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);
    }
}

Kemudian controller saya terlihat seperti

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));
    }
}

Jadi saya pikir saya menggunakan pola desain dengan benar dan menjaga praktik yang baik, tetapi masalah dengan hal ini adalah ketika saya menambahkan tabel baru, saya harus membuat kelas yang sama hanya dengan nama lain. Ini menimbulkan kecurigaan pada saya bahwa saya mungkin melakukan sesuatu yang salah.

Saya memikirkan solusi di mana alih-alih antarmuka saya memiliki kelas abstrak yang menggunakan nama kelas mencari tahu tabel yang mereka butuhkan untuk memanipulasi tetapi itu sepertinya bukan hal yang tepat untuk dilakukan, bagaimana jika saya memutuskan untuk beralih ke penyimpanan file atau memcache di mana tidak ada tabel.

Apakah saya mendekati ini dengan benar, atau adakah perspektif berbeda yang harus saya lihat?

Emilio Rodrigues
sumber
Saat Anda membuat tabel baru, apakah Anda selalu menggunakan set kueri SQL yang sama (atau set yang sangat mirip) untuk berinteraksi dengannya? Juga, apakah Pabrik merangkum logika yang berarti dalam program nyata?
Ixrec
@ Ixrec umumnya akan ada metode khusus di gateway dan repositori yang melakukan kueri sql yang lebih kompleks seperti gabungan, masalahnya adalah bahwa fungsi persist, retve dan delete - yang ditentukan oleh antarmuka - selalu sama kecuali untuk nama tabel dan mungkin tetapi tidak mungkin kolom kunci utama, jadi saya harus mengulanginya di setiap implementasi. Pabrik sangat jarang memegang logika apa pun dan kadang-kadang saya melewatkannya sama sekali dan meminta gateway mengembalikan objek alih-alih data, tetapi saya membuat pabrik untuk contoh ini karena seharusnya desain yang tepat?
Emilio Rodrigues
Saya mungkin tidak memenuhi syarat untuk memberikan jawaban yang tepat, tetapi saya mendapat kesan bahwa 1) kelas-kelas Pabrik dan Repositori tidak benar-benar melakukan sesuatu yang bermanfaat, jadi Anda akan lebih baik membuangnya dan bekerja hanya dengan Komentar dan Komentar langsung. 2) Seharusnya dimungkinkan untuk menempatkan fungsi bertahan / mengambil / menghapus yang umum di satu tempat daripada menyalin-menempelnya, mungkin dalam kelas abstrak "implementasi default" (agak seperti apa Koleksi di Jawa lakukan)
Ixrec

Jawaban:

12

Masalah yang Anda tangani cukup mendasar.

Saya telah mengalami masalah yang sama ketika saya bekerja untuk sebuah perusahaan yang membuat aplikasi J2EE besar yang terdiri dari beberapa ratus halaman web dan lebih dari satu juta setengah baris kode Java. Kode ini menggunakan ORM (JPA) untuk kegigihan.

Masalah ini menjadi lebih buruk ketika Anda menggunakan teknologi pihak ke-3 di setiap lapisan arsitektur dan semua teknologi membutuhkan representasi data mereka sendiri.

Masalah Anda tidak dapat diselesaikan pada tingkat bahasa pemrograman yang Anda gunakan. Menggunakan pola itu baik tetapi seperti yang Anda lihat menyebabkan pengulangan kode (lebih tepatnya: pengulangan desain).

Cara saya melihatnya hanya ada 3 solusi yang mungkin. Dalam praktiknya solusi ini turun ke hal yang sama.

Solusi 1: Gunakan beberapa kerangka kerja ketekunan lainnya yang memungkinkan Anda untuk hanya menyatakan apa yang harus dipertahankan. Mungkin ada semacam kerangka kerja di sekitar. Masalah dengan pendekatan ini adalah agak naif karena tidak semua pola Anda terkait dengan kegigihan. Anda juga ingin menggunakan pola untuk kode antarmuka pengguna sehingga Anda akan membutuhkan kerangka kerja GUI yang dapat menggunakan kembali representasi data dari kerangka kerja ketekunan yang Anda pilih. Jika Anda tidak dapat menggunakannya kembali, Anda harus menulis kode pelat ketel untuk menjembatani representasi data kerangka GUI dan kerangka kerja bertahan .. dan ini bertentangan dengan prinsip KERING lagi.

Solusi 2: Gunakan bahasa pemrograman lain - yang lebih kuat - yang memiliki konstruksi yang memungkinkan Anda untuk mengekspresikan desain berulang sehingga Anda dapat menggunakan kembali kode desain. Ini mungkin bukan pilihan bagi Anda, tetapi anggaplah untuk sesaat. Kemudian lagi ketika Anda mulai membuat antarmuka pengguna di atas lapisan ketekunan Anda akan ingin bahasa lagi menjadi cukup kuat untuk mendukung pembuatan GUI tanpa harus menulis kode pelat ketel. Tidak mungkin ada bahasa yang cukup kuat untuk melakukan apa yang Anda inginkan karena sebagian besar bahasa bergantung pada kerangka kerja pihak ketiga untuk membangun GUI yang masing-masing memerlukan representasi data mereka sendiri untuk bekerja.

Solusi 3: Mengotomatiskan pengulangan kode dan desain menggunakan beberapa bentuk pembuatan kode. Kekhawatiran Anda adalah harus mengulangi pola dan desain karena kode tangan berulang-ulang kode / desain melanggar prinsip KERING. Saat ini ada kerangka pembuat kode yang sangat kuat di luar sana. Bahkan ada "meja kerja bahasa" yang memungkinkan Anda dengan cepat (setengah hari ketika Anda tidak memiliki pengalaman) membuat bahasa pemrograman Anda sendiri dan menghasilkan kode apa pun (PHP / Java / SQL - file teks apa pun yang dapat dipikirkan) menggunakan bahasa itu. Saya memiliki pengalaman dengan XText tetapi MetaEdit dan MPS juga baik-baik saja. Saya sangat menyarankan Anda untuk memeriksa salah satu meja kerja bahasa ini. Bagi saya itu adalah pengalaman paling membebaskan dalam kehidupan profesional saya.

Menggunakan Xtext Anda dapat membuat mesin Anda menghasilkan kode berulang. Xtext bahkan menghasilkan editor penyorotan sintaks untuk Anda dengan penyelesaian kode untuk spesifikasi bahasa Anda sendiri. Dari titik itu, Anda cukup mengambil gerbang dan kelas pabrik dan mengubahnya menjadi templat kode dengan meninju lubang di dalamnya. Anda memberi mereka makan ke generator Anda (yang disebut oleh parser bahasa Anda yang juga sepenuhnya dihasilkan oleh Xtext) dan generator akan mengisi lubang di template Anda. Hasilnya adalah kode yang dihasilkan. Dari titik itu Anda dapat melakukan pengulangan kode di mana saja (kode kegigihan kode GUI, dll.).

Chris-Jan Twigt
sumber
Terima kasih atas jawabannya, saya serius mempertimbangkan pembuatan kode dan saya bahkan mulai mengimplementasikan solusi. Mereka adalah 4 kelas boilerplate jadi saya kira saya bisa melakukannya di PHP itu sendiri. Meskipun ini tidak memecahkan masalah kode berulang saya pikir bahwa pengorbanan itu layak - memiliki sangat dipelihara dan mudah diubah meskipun kode berulang.
Emilio Rodrigues
Ini adalah yang pertama saya dengar tentang XText dan tampilannya sangat kuat. Terima kasih membuat saya sadar akan hal ini!
Matthew James Briggs
8

Masalah yang Anda hadapi adalah masalah lama: kode untuk objek persisten sering terlihat serupa untuk setiap kelas, itu hanya kode boilerplate. Itulah sebabnya beberapa orang pintar menemukan Object Relational Mappers - mereka memecahkan masalah itu dengan tepat. Lihat posting SO sebelumnya untuk daftar ORM untuk PHP.

Ketika ORM yang ada tidak menderita kebutuhan Anda, ada juga alternatif: Anda dapat menulis generator kode Anda sendiri, yang mengambil deskripsi meta objek Anda untuk bertahan dan menghasilkan bagian berulang kode dari itu. Itu sebenarnya tidak terlalu sulit, saya melakukan ini di masa lalu untuk beberapa bahasa pemrograman yang berbeda, saya yakin itu juga akan mungkin untuk mengimplementasikan hal-hal seperti itu juga di PHP.

Doc Brown
sumber
Saya telah membuat fungsi seperti itu tetapi saya beralih dari ini ke ini karena saya dulu memiliki objek data menangani tugas-tugas kegigihan data yang tidak sesuai dengan SRP. Sebagai contoh saya dulu punya Model::getByPKmetode dan dalam contoh di atas saya akan bisa melakukan Comment::getByPKtetapi mendapatkan data dari database dan membangun objek semua terkandung dalam kelas objek data, yang merupakan masalah yang saya coba pecahkan dengan menggunakan pola desain .
Emilio Rodrigues
ORM tidak harus menempatkan logika persistensi dalam objek model. Ini adalah pola Rekaman Aktif, dan sementara populer ada alternatif. Lihatlah ORM apa yang tersedia, dan Anda harus menemukan yang tidak memiliki masalah ini.
Jules
@ Jules itu adalah poin yang sangat bagus, itu membuat saya berpikir dan saya bertanya-tanya - apa masalah dengan memiliki ActiveRecord dan implementasi Data Mapper yang tersedia di aplikasi saya. Lalu saya bisa menggunakan masing-masing dari mereka ketika saya membutuhkannya - ini akan menyelesaikan masalah saya penulisan ulang kode yang sama dengan menggunakan pola ActiveRecord dan kemudian ketika saya benar-benar membutuhkan mapper data, tidak akan merepotkan untuk membuat kelas yang diperlukan untuk pekerjaan?
Emilio Rodrigues
1
Satu-satunya masalah yang dapat saya lihat dengan ini sekarang adalah bahwa mengerjakan tepi kasus ketika permintaan harus bergabung dengan dua tabel di mana satu menggunakan Rekaman Aktif dan yang lainnya dikelola oleh Data Mapper Anda - itu akan menambah lapisan kompleksitas yang sebaliknya tidak akan akan muncul. Secara pribadi, saya hanya menggunakan mapper - Saya tidak pernah menyukai Rekaman Aktif dari awal - tapi saya tahu itu hanya pendapat saya, dan yang lainnya tidak setuju.
Jules