İyi uygulamalarda KURU prensibi?


11

Programlamamda olabildiğince DRY ilkesini izlemeye çalışıyorum. Son zamanlarda OOP'ta tasarım kalıpları öğreniyorum ve kendimi bir sürü tekrarladım.

Kalıcılığımı işlemek için bir Fabrika ve Ağ Geçidi desenleri ile birlikte bir Depo deseni oluşturdum. Uygulamamda bir veritabanı kullanıyorum, ancak Gateway'i değiştirebilmem ve istersem kalıcılığın başka bir türüne geçebilmem gerektiğinden bu önemli değil.

Kendim için oluşturduğum sorun, sahip olduğum tablo sayısı için aynı nesneleri oluşturmam. Örneğin bunlar bir tabloyu işlemem gereken nesneler olacak 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);
    }
}

Sonra kontrol cihazım şöyle görünüyor

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

Bu yüzden tasarım desenlerini doğru bir şekilde kullandığımı ve iyi uygulamaları koruduğumu düşündüm, ancak bu sorunla ilgili sorun, yeni bir tablo eklediğimde, aynı sınıfları diğer adlarla oluşturmak zorunda olduğum. Bu, yanlış bir şey yaptığım konusunda içimde şüphe uyandırıyor.

Arabirimler yerine, sınıf adını kullanarak manipüle etmeleri gereken tabloyu anlayan soyut sınıfları olan bir çözüm düşündüm, ancak bu doğru bir şey gibi görünmüyor, ya bir dosya deposuna geçmeye karar verirsem ya da tablo olmayan memcache.

Buna doğru yaklaşıyor muyum yoksa bakmam gereken farklı bir bakış açısı var mı?


Yeni bir tablo oluşturduğunuzda, tabloyla etkileşim kurmak için her zaman aynı SQL sorguları kümesini (veya son derece benzer bir kümeyi) kullanır mısınız? Ayrıca, Fabrika gerçek programda anlamlı bir mantık kapsülliyor mu?
Ixrec

@Ixrec genellikle ağ geçidi ve depoda birleşimler gibi daha karmaşık sql sorguları gerçekleştiren özel yöntemler olacaktır, sorun, arabirim tarafından tanımlanan kalıcı, alma ve silme işlevlerinin tablo adı dışında her zaman aynı olması ve muhtemelen ancak birincil anahtar sütununun olası olmaması nedeniyle, her uygulamada bunları tekrarlamak zorundayım. Fabrika çok nadiren herhangi bir mantığı tutar ve bazen atlar ve ağ geçidinin veri yerine nesneyi döndürmesini isterim, ancak uygun tasarım olması gerektiğinden bu örnek için bir fabrika oluşturdum ?
Emilio Rodrigues

Muhtemelen uygun bir cevap vermek için nitelikli değilim, ancak 1) Fabrika ve Depo sınıflarının gerçekten yararlı bir şey yapmadığı izlenimini edindim, bu yüzden onları atmaktan ve doğrudan Comment ve CommentGateway ile çalışmaktan daha iyi olur 2) Ortak kalıcı / geri alma / silme işlevlerini, belki de "varsayılan uygulamaların" soyut bir sınıfına (Java'daki Koleksiyonların yaptığı gibi) tek bir yere koymak mümkün olmalıdır
Ixrec

Yanıtlar:


12

Ele aldığınız sorun oldukça temel.

Birkaç yüz web sayfası ve bir milyon buçuk satırdan fazla Java kodu içeren büyük bir J2EE uygulaması yapan bir şirkette çalışırken de aynı sorunu yaşadım. Bu kod kalıcılık için ORM (JPA) kullanmıştır.

Mimarinin her katmanında 3. taraf teknolojilerini kullandığınızda ve teknolojilerin tümü kendi veri gösterimlerini gerektirdiğinde bu sorun daha da kötüleşir.

Sorununuz kullandığınız programlama dili düzeyinde çözülemiyor. Kalıpları kullanmak iyidir, ancak gördüğünüz gibi kodun tekrarlanmasına neden olur (daha doğru cevher koymak: tasarımların tekrarlanması).

Gördüğüm gibi sadece 3 olası çözüm var. Uygulamada bu çözümler aynıdır.

Çözüm 1: Yalnızca neyin kalıcı olması gerektiğini belirtmenize izin veren başka bir kalıcılık çerçevesi kullanın . Muhtemelen böyle bir çerçeve var. Bu yaklaşımdaki sorun, oldukça naif olmasıdır, çünkü tüm desenleriniz sebatla ilgili olmayacaktır. Ayrıca, kullanıcı arabirimi kodu için kalıplar kullanmak isteyeceksiniz, böylece seçtiğiniz kalıcılık çerçevesinin veri temsillerini yeniden kullanabilen bir GUI çerçevesine ihtiyacınız olacaktır. Bunları tekrar kullanamıyorsanız, GUI çerçevesinin veri gösterimlerini ve kalıcılık çerçevesini köprülemek için kazan plakası kodu yazmanız gerekir. Ve bu yine KURU ilkesine aykırıdır.

Çözüm 2: Tasarım kodunu yeniden kullanabilmeniz için tekrarlayan tasarımı ifade etmenizi sağlayan yapılara sahip başka bir - daha güçlü - programlama dili kullanın. Bu muhtemelen sizin için bir seçenek değil, bir an için varsayın. Daha sonra, kalıcılık katmanının üstünde bir kullanıcı arayüzü oluşturmaya başladığınızda, dilin tekrar kazan plakası kodu yazmak zorunda kalmadan GUI oluşturmayı destekleyecek kadar güçlü olmasını istersiniz. Çoğu dil, GUI oluşturma için her biri kendi veri temsilinin çalışması için gerekli olan üçüncü taraf çerçevelerine bağlı olduğundan, istediğinizi yapacak kadar güçlü bir dil olması pek olası değildir.

3.Çözüm: Bir tür kod oluşturma kullanarak kodun ve tasarımın tekrarlanmasını otomatikleştirin. Endişeniz, kalıpları tekrarlayan kod / tasarım DRY ilkesini ihlal ettiğinden, desen ve tasarımların tekrarlarını el ile kodlamak zorunda olmakla ilgilidir. Günümüzde çok güçlü kod üreteci çerçeveleri var. Hatta hızlı bir şekilde (deneyiminiz olmadığında yarım gün) kendi programlama dilinizi oluşturmanıza ve bu dili kullanarak herhangi bir kod (PHP / Java / SQL - herhangi bir düşünülebilir metin dosyası) oluşturmanıza izin veren "dil çalışma tezgahları" bile vardır. XText ile deneyimim var ama MetaEdit ve MPS de iyi görünüyor. Bu dil çalışma tezgahlarından birine göz atmanızı şiddetle tavsiye ediyorum. Benim için profesyonel hayatımdaki en özgürleştirici deneyimdi.

Xtext'i kullanarak makinenizin tekrarlayan kodu üretmesini sağlayabilirsiniz. Xtext, kendi dil spesifikasyonunuz için kod tamamlama ile sizin için bir sözdizimi vurgulama düzenleyicisi bile oluşturur. Bu noktadan sonra, ağ geçidinizi ve fabrika sınıfınızı alıp, delikleri açarak kod şablonlarına dönüştürürsünüz. Onları jeneratörünüze beslersiniz (Xtext tarafından tamamen oluşturulan bir dil ayrıştırıcısı tarafından çağrılır) ve jeneratör şablonlarınızdaki delikleri dolduracaktır. Sonuç, oluşturulan koddur. Bu noktadan sonra herhangi bir yerde kod tekrarını (GUI kodu kalıcılık kodu vb.) Çıkarabilirsiniz.


Cevabınız için teşekkür ederim, kod üretimini ciddiye aldım ve hatta bir çözüm uygulamaya başlıyorum. Onlar 4 boilerplate sınıfları, bu yüzden PHP kendisi bunu yapabilirdi sanırım. Bu yinelenen kod sorunu çözmese de ben tradeoffs buna değer olduğunu düşünüyorum - yinelenen kod rağmen son derece sürdürülebilir ve kolayca değiştirilebilir olması.
Emilio Rodrigues

Bu XText'i ilk duydum ve çok güçlü görünüyor. Beni bu konuda bilgilendirdiğin için teşekkürler!
Matthew James Briggs

8

Karşılaştığınız sorun eskidir: kalıcı nesneler için kod genellikle her sınıf için benzerdir, basitçe kaynak kodudur. Bu yüzden bazı akıllı insanlar Nesne İlişkisel Haritacıları icat ettiler - tam olarak bu sorunu çözüyorlar . PHP için ORM'lerin listesi için bu eski SO yayınına bakın .

Mevcut ORM'ler ihtiyaçlarınızı çekmediğinde, bir alternatif de vardır: kalıcı olması için nesnelerinizin meta açıklamasını alan ve kodun yinelenen kısmını oluşturan kendi kod üretecinizi yazabilirsiniz. Bu aslında çok zor değil, bunu geçmişte bazı farklı programlama dilleri için yaptım, eminim PHP'de de bu tür şeyleri uygulamak mümkün olacak.


Ben böyle bir işlevsellik oluşturdum ama veri nesnesi SRP ile uyumlu olmayan veri kalıcılık görevlerini işlemek için bu çünkü bu geçiş. Örneğin Model::getByPKyöntem kullandım ve yukarıdaki örnekte yapabilirim Comment::getByPKama veritabanından veri almak ve nesneyi oluşturmak tüm veri nesnesi sınıfında yer almaktadır, bu da tasarım desenleri kullanarak çözmeye çalışıyorum sorun .
Emilio Rodrigues

ORM'lerin kalıcılık mantığını model nesnesine yerleştirmesi gerekmez. Bu Aktif Kayıt modelidir ve popüler olsa da alternatifler vardır. Hangi ORM'lerin kullanılabilir olduğuna bir göz atın ve bu sorunu olmayan birini bulmalısınız.
Jules

@Jules bu çok iyi bir nokta, beni düşündürdü ve merak ediyorum - benim uygulamada bir ActiveRecord ve bir Veri Eşleyici uygulamaları olması sorunu ne olurdu. Sonra onlara ihtiyacım olduğunda her birini kullanabilirsiniz - bu ActiveRecord desenini kullanarak aynı kodu yeniden yazma sorunumu çözecek ve sonra bir veri eşleştirici gerekir zaman gerekli sınıfları oluşturmak için böyle bir acı olmaz iş için?
Emilio Rodrigues

1
Şu anda görebildiğim tek sorun, bir sorgunun biri Aktif Kayıt kullanan ve diğeri Veri Eşleyici tarafından yönetilen iki tabloya katılması gerektiğinde uç durumlarda çalışmaktır, aksi takdirde istemeyen bir karmaşıklık katmanı ekler. ortaya çıkmaz. Şahsen ben sadece haritacıyı kullanardım - En başından beri Aktif Kayıt'ı hiç sevmedim - ama bunun sadece benim fikrim olduğunu biliyorum ve diğerleri de aynı fikirde değil.
Jules
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.