Tek Sorumluluk İlkesi ile Mücadele


11

Bu örneği düşünün:

Bir web sitem var. Kullanıcıların yayın yapmasına (herhangi bir şey olabilir) ve yayını tanımlayan etiketler eklemesine olanak tanır. Kodda, yazı ve etiketleri temsil eden iki sınıf var. Bu sınıflara Postve diyelim Tag.

Postyayın oluşturma, yayınları silme, yayınları güncelleme, vb. ile Tagilgilenir. etiket oluşturma, etiketleri silme, etiketleri güncelleme vb.

Eksik bir işlem var. Etiketlerin gönderilere bağlanması. Bu operasyonu kimin yapması gerektiğiyle mücadele ediyorum. Her iki sınıfa da eşit derecede uygun olabilir.

Bir yandan Postsınıf Tag, a parametresini alan ve daha sonra onu bir etiket listesinde saklayan bir işleve sahip olabilir . Öte yandan, Tagsınıf bir alır bir işlevi olabilir Postparametre olarak ve bağlantılar Tagiçin Post.

Yukarıdaki sorunumun sadece bir örneğidir. Aslında buna benzer birden çok sınıf ile koşuyorum. Her ikisine de eşit şekilde sığabilir. Aslında her iki sınıfa da işlevsellik katmaktan yoksun, bu sorunu çözmeme yardımcı olacak kurallar veya tasarım stilleri var. Ben sadece bir tane seçmek için kısa bir şey olması gerektiğini varsayıyorum?

Belki de her iki sınıfa koymak doğru cevaptır?

Yanıtlar:


11

Korsan Kodu gibi, SRP de bir kuraldan çok bir kılavuzdur ve özellikle iyi yazılmış bir kural değildir. Çoğu geliştirici, Martin Fowler'ın ( Refactoring'de ) ve Robert Martin'in ( Temiz Kod'da ) yeniden tanımlarını kabul etmiştir, bu da bir sınıfın (bir sorumluluğun aksine) değişmek için sadece bir nedeni olması gerektiğini düşündürmektedir .

İyi, sağlam (punta bahane) bir rehberdir, ancak görmezden gelmek kadar ona asılmak neredeyse tehlikelidir.

Bir Etikete bir Yazı ekleyebilir veya bunun tersini yapabilirseniz, tek sorumluluk ilkesini ihlal etmediniz. Her ikisinin de değiştirmek için sadece bir nedeni var - eğer o nesnenin yapısı değişirse. İkisinden birinin yapısını değiştirmek diğerine ekleme şeklini değiştirmez, bu yüzden ona yeni bir "sorumluluk" eklemezsiniz.

Nihai kararınız gerçekten ön uçta gerekli işlevsellik tarafından belirlenmelidir. Muhtemelen bir noktada bir Gönderiye Etiket eklemeniz gerekecektir, bu yüzden aşağıdakine benzer bir şey yapın:

// C-style-language pseudo-code
class Post {
    string _title;
    string _content;
    Date _date;
    List<Tag> _tags;

    Post(string title, string content) {
        _title = title;
        _content = content;
        _date = Now;
        _tags = new List<Tag>();
    }

    Tag[] getTags() {
        return _tags.toArray();
    }

    void addTag(Tag tag) {
        if (_tags.contains(tag)) {
            throw "Cannot add tag twice";
        }

        _tags.Add(tag);
        tag.referencePost(this);
    }

    // more stuff here, obviously
}

class Tag {
    string _name;
    List<Post> _posts;

    Tag(string name) {
        _name = name;
    }

    Post[] getPosts() {
        return _posts.toArray();
    }

    void referencePost(Post post) {
        if (!post.getTags().contains(this) || _posts.contains(post)) {
            throw "Only reference a post by calling Post.addTag()";
        }

        _posts.Add(post);
    }

    // more stuff here too
}

Daha sonra, Etiketlere Mesajlar da eklemeniz gerekirse, Tag sınıfına bir addPost yöntemi ve Post sınıfına bir referenceTag yöntemi eklemeniz yeterlidir. Açıkçası, addTost gelen addTag ve addTag gelen addPost çağırarak yanlışlıkla bir yığın taşmasına neden olmaz böylece farklı adlandırılmış.


Tag ve Post arasındaki ilişkinin çoktan çoğa olduğunu düşünüyorum, bu durumda bir Tag'in birden fazla gönderiye referans tutması mantıklıdır. Tek bir referans tutarsanız bunu nasıl ele alırsınız?
Andres F.

@AndresF .: Sana katılıyorum, bu yüzden cevabımı çok iyi yazmadım. Önemli ölçüde düzenleme yaptım. (Bu, gördüğünüz gibi anlamı değiştirirse önceki yükselticiden özür dileriz.)
pdr

6

Hayır, ikisinde de değil! Tek bir yerde olmalı.

Sorunuzda rahatsız edici bulduğum şey, " Postyayın oluşturma, yayınları silme, yayınları güncelleme" ve aynı şeyleri söylemenizdir Tag. Evet, bu doğru değil. Postsadece güncellemeyle ilgilenebilir, aynı Tag. Yaratmak ve silmek, ( Postve Tagsadece diyelim) dışında bir başkasının çalışmasıdır Store.

İyi sorumluluğu Post"yazarını, içeriğini ve son güncelleme tarihini bilir" dir. İyi sorumluluğu Tag"adını ve amacını bilir (oku: açıklama)". İyi sorumluluk Store"tüm Yazıları ve tüm Etiketleri bilir ve ekleyebilir, kaldırabilir ve arayabilir" dir.

Bu üç katılımcıya bakarsanız, Post-Tag ilişkisi hakkında bilgi sahibi olması gereken doğal olarak kimdir?

(benim için, bu Post, "etiketlerini bildiği" doğal görünüyor; ters arama (bir etiket için tüm gönderiler) Mağaza'nın işi gibi görünüyor; yanılmış olabilirim)


Her gönderi bir etiket listesine sahipse ve / veya her bir etiketin içinde yer aldığı bir gönderi listesi varsa, "gönderi x etiket y içerir" sorusunu kolayca yanıtlayabilir. Böyle bir soruyu herhangi bir sınıftan sorumluluk almadan, etkili bir şekilde cevaplamanın bir yolu ConditionalWeakTablevar mı?
supercat

3

Denklemde eksik olan önemli bir detay var. Etiket neden posta ve tersi içeriyor? Bu sorunun cevabı verilen her set için çözümü belirler.

Genel olarak benzer bir durum düşünebilirim. Bir kutu ve içindekiler. Bir kutunun içeriği vardır, bu nedenle has-a ilişkisi uygundur. İçeriğin bir kutusu olabilir mi? Tabii, bir kutu ile bir kutu. Bir kutu içeriktir. Ancak IS-A, tüm kutular için iyi bir tasarım değildir. Böyle bir durumda dekoratör desenini düşünürdüm. Bu şekilde bir kutu çalışma zamanında içerikle gerektiği gibi dekore edilir.

Etiketler de Gönderiler içerebilir, ancak bana göre bu statik bir ilişki değil. Aksine, bu etiketi taşıyan tüm yayınların bir raporu olabilir. Bu durumda yeni bir varlıktır, has-a değil.


2

Teoride böyle şeyler her iki yönde de gidebilirken, pratikte uygulamaya geçtiğinizde bir yol neredeyse her zaman diğerinden daha iyi bir seçimdir. Benim öyküm Postsınıfta daha iyi sığacak çünkü yazı oluşturma veya düzenleme sırasında, yazı ile ilgili diğer şeyler aynı anda değiştiğinde ilişkilendirme oluşturulacak.

Ayrıca, birden fazla etiketi ilişkilendiriyorsanız ve bunu tek bir veritabanı güncellemesinde yapmak istiyorsanız, güncellemeyi yapmadan önce aynı gönderiyle ilişkili tüm etiketlerin bir tür listesini oluşturmanız gerekir. Bu liste Postsınıfta çok daha iyi uyuyor .


1

Şahsen ben bu işlevselliği hiçbirine eklemezdim.

Bana göre, her ikisi de Postve Tagveri nesneleridir, bu yüzden veritabanı işlevselliğini işlememelidir. Sadece var olmalılar. Verileri tutmaları ve uygulamanızın diğer bölümleri tarafından kullanılması amaçlanmıştır.

Bunun yerine, iş mantığınızdan ve web sayfanızla ilgili verilerinizden sorumlu başka bir sınıfım daha olurdu. Sayfanız bir yazı gösteren ve kullanıcıların etiket eklemek için izin ise, o sınıf bir olurdu Postnesne ve ekleme işlevini içermesi Tagsbuna Post. Sayfa etiketlerinizi görüntülendiği ve kullanıcıların bu etiketlere mesajları eklemek için izin ise, bir içerecektir Tagnesne ve eklenecek özelliğe sahip Postsolduğu için Tag.

Gerçi sadece ben. Veri nesnelerinizde veritabanı işlevselliğini işlemeniz gerektiğini düşünüyorsanız, pdr'nin yanıtını öneriyorum


0

Bu soruyu okurken akla ilk gelen çok fazla veritabanı ilişkisi oldu. Gönderilerin birçok Etiketi olabilir ... Etiketlerin birçok İletisi olabilir .... Her iki sınıfın da bu ilişkiyi bir ölçüde yönetme yeteneğine ihtiyacı olduğu anlaşılıyor.

Görüş Mesaj açıdan ...
senin düzenleme veya bir oluştururken Yayınla ikincil etkinlik yönetme olur Etiket ilişkileri.

  1. Yayına Mevcut Etiketi Ekle
  2. Etiketi Gönderiden Döndür

IMO, tamamen yeni bir TAG'ın oluşturulması buraya ait değil.

Etiket bakış açısından ... Bir Post'a atamak zorunda kalmadan
bir Etiket oluşturabilirsiniz . Bir Gönderi ile etkileşime girdiğini gördüğüm tek etkinlik Etiketi Sil işlevidir. Ancak, bu işlev bağımsız bir bağımsız işlev olmalıdır.

Bu yalnızca Çoktan Çoka ilişkisini çözen bir veritabanı bağlantı tablosu varsa çalışır

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.