Nesneye Dayalı Tasarımda Gevşek Kavrama


16

GRASP öğrenmeye çalışıyorum ve bu düşük kuplaj hakkında açıkladı ( burada sayfa 3 ) ve bunu bulduğumda çok şaşırdım:

addTrackBir Albumsınıfın yöntemini düşünün , iki olası yöntem şunlardır:

addTrack( Track t )

ve

addTrack( int no, String title, double duration )

Hangi yöntem kuplajı azaltır? İkincisi, Album sınıfını kullanan sınıfın bir Track sınıfı bilmek zorunda olmadığı için. Genel olarak, yöntem parametreleri, java. * Paketlerinden temel türleri (int, char ...) ve sınıfları kullanmalıdır.

Ben buna katlanıyorum; Çeşitli nedenlerden addTrack(Track t)daha iyi olduğuna inanıyorum addTrack(int no, String title, double duration):

  1. Bir yöntemin mümkün olduğu kadar az parametreye sahip olması her zaman daha iyidir (Bob Amca'nın Temiz Koduna göre hiçbiri veya bir, tercihen bazı durumlarda 2 ve özel durumlarda 3; Yeniden düzenlemeye ihtiyaç vardır - bunlar elbette kutsal kurallar değildir) .

  2. Bir addTrackarabirim yöntemiyse ve gereksinimlerin a'nın Trackdaha fazla bilgiye sahip olması gerekir (örneğin yıl veya tür), arabirimin değiştirilmesi gerekir ve böylece yöntemin başka bir parametreyi desteklemesi gerekir.

  3. Kapsülleme kırılmıştır; eğer addTrackbir arayüzdeyse, o cihazın içini bilmemelidir Track.

  4. Aslında birçok yönden ikinci yolla daha fazla birleşmiştir. Varsayalım noparametre ihtiyaçlarını değiştirilebilir için intiçin longfazla olduğundan MAX_INTparça (veya herhangi bir nedenle); bu durumda hem ve Trackhem de yöntemin değiştirilmesi gerekirken, yöntem addTrack(Track track)yalnızca bu Trackyöntemse değişecektir.

4 argümanın hepsi aslında birbiriyle bağlantılıdır ve bazıları diğerlerinin sonuçlarıdır.

Hangi yaklaşım daha iyi?


2
Bu bir profesör veya eğitmen tarafından bir araya getirilmiş bir belge mi? Sağladığınız bağlantının URL'sine göre, bir sınıf için olduğu anlaşılıyor, ancak belgede kimin oluşturduğuna dair herhangi bir kredi görmüyorum. Bu bir sınıfın parçasıysa, belgeyi sağlayan kişinin bu sorularını sormanızı öneririm. Bu arada akıl yürütmenize katılıyorum - bir Album sınıfının doğası gereği bir Track sınıfı hakkında bilgi edinmek istediği anlaşılıyor.
Derek

Dürüst olmak gerekirse, "En İyi Uygulamalar" hakkında her okuduğumda, onları bir tane tuzla alıyorum!
AraK

@Derek Google'da "desen kavrama örneği" için arama yaparak belgeyi buldum; Kim yazmış değilim ama üniversiteden beri güvenilir olduğuna inanıyorum. Verilen bilgilere dayanarak bir örnek arıyorum ve kaynağı görmezden geliyorum.
m3th0dman

4
@ m3th0dman "ama üniversiteden beri güvenilir olduğuna inanıyorum." Benim için bir üniversiteden olduğu için, güvenilir olmadığını düşünüyorum. Yazılım geliştirmedeki en iyi uygulamalar hakkında konuşan çok yıllı projelerde çalışmayan birine güvenmiyorum.
AraK

1
@AraK Güvenilir tartışılmaz anlamına gelmez; ve ben burada bunu yapıyorum, sorgulayarak.
m3th0dman

Yanıtlar:


15

İlk üç noktanız aslında çiftlenmeden başka ilkelerle ilgilidir. Çelişkili tasarım ilkeleri arasında daima bir denge kurmalısınız.

Sizin dördüncü nokta olan birleştirilmesi konusunda ve şiddetle katılıyorum. Kuplaj, modüller arasındaki veri akışı ile ilgilidir . Verilerin aktığı kabın türü büyük ölçüde önemsizdir. Bir alanı yerine iki katı olarak Trackgeçmek, onu geçme ihtiyacını ortadan kaldırmaz. Modüllerin hala aynı miktarda veri paylaşması ve yine de aynı miktarda bağlantıya sahip olması gerekir.

Ayrıca sistemdeki tüm kaplinleri bir agrega olarak görmüyor. Bir Tracksınıfı tanıtmak, iki ayrı modül arasında başka bir bağımlılık eklerken , buradaki önemli önlem olan sistemin birleşmesini önemli ölçüde azaltabilir .

Örneğin, bir "Çalma Listesine Ekle" düğmesini ve bir Playlistnesneyi düşünün . Bir Tracknesneyi tanıtmak, yalnızca bu iki nesneyi dikkate alırsanız kuplajı artırdığı düşünülebilir. Artık iki yerine birbirine bağımlı üç sınıfınız var. Ancak, bu sisteminizin tamamı değildir. Ayrıca parçayı içe aktarmanız, parçayı oynatmanız, parçayı görüntülemeniz vb. Gerekir. Bu karışıma bir sınıf daha eklemek önemsizdir.

Şimdi şarkıları yalnızca yerel olarak değil ağ üzerinden oynatmak için destek eklemeyi düşünün. Sadece NetworkTrackaynı arabirime uyan bir nesne oluşturmanız gerekir . TrackNesne olmadan, aşağıdaki gibi her yerde işlevler oluşturmanız gerekir:

addNetworkTrack(int no, string title, double duration, URL location)

Bu, bağlantınızı etkili bir şekilde iki katına çıkarır, ancak ağa özgü şeyleri umursamayan modüller bile yine de takip edebilmek için onu geçirebilmek için gerektirir.

Dalgalanma etkisi testiniz, gerçek kuplaj miktarınızı belirlemek için iyi bir testtir. Endişe duyduğumuz şey, bir değişikliğin etkilediği yerleri sınırlamaktır.


1
+ İlkellere bağlanma, nasıl dilimlendiğine bakılmaksızın hala bağlantı kuruyor.
JustinC

URL ekle seçeneği / dalgalanma efektinden bahsettiğiniz için +1.
user949300

4
+1 Bu konuda ilginç bir okuma da , ilkel tiplerin kullanımının aslında Değer Nesnesi ile düzeltme olarak bir İlkel Saplantı "kokusu" olarak görüldüğü vahşi doğada DIP'de Bağımlılık Ters Çevirme İlkesi'nin tartışılması olacaktır . Bana göre, ilkel tiplerden oluşan bir gruptan oluşan bir Track nesnesini geçmek daha iyi olurdu ... Ve belirli sınıflara bağımlılık / bağlantı kurmaktan kaçınmak istiyorsanız arayüzleri kullanın.
Marjan Venema

Toplam sistem kuplajı ile modül kuplajı arasındaki fark hakkında güzel açıklama nedeniyle kabul edilen cevap
m3th0dman

10

Benim tavsiyem:

kullanım

addTrack( ITrack t )

ancak ITracksomut bir sınıf değil, bir arayüz olduğundan emin olun .

Albüm uygulayıcıların içini bilmiyor ITrack. Sadece tarafından tanımlanan sözleşmeye bağlıdır ITrack.

Sanırım bu, en az miktarda kuplaj üreten çözüm.


1
Ben Track sadece basit bir fasulye / veri aktarım nesnesi olduğuna inanıyorum, sadece üzerinde alanları ve alıcıları / ayarlayıcıları vardır; bu durumda bir arayüz gerekli mi?
m3th0dman

6
Gereklidir? Muhtemelen değil. Müstehcen, evet. Bir pistin somut anlamı gelişebilir ve gelişecektir, ancak tüketen sınıfın ondan istediği şey muhtemelen olmayacaktır.
JustinC

2
@ m3th0dman Her zaman soyutlamalara bağlıdır, betonlara değil. Bu Trackaptal ya da akıllı olmasına bakılmaksızın uygulanır . Trackbir somutlaştırmadır. ITrackarayüz bir soyutlamadır. Bu şekilde, gelecekte uydukları sürece farklı Parça türlerine sahip olursunuz ITrack.
Tulains Córdova

4
Bu fikre katılıyorum, ancak 'I' önekini kaybettim. Clean Code'dan Robert Martin, sayfa 24: "Bugünün eski wads'larında çok yaygın olan önceki I, en iyi ihtimalle dikkat dağıtıcı ve çok kötü bilgiler. Kullanıcılarımın onlara teslim ettiğimi bilmelerini istemiyorum arayüz."
Benjamin Brumfield

1
@BenjaminBrumfield Haklısın. Öneki de sevmiyorum, ancak netlik için cevapta bırakacağım.
Tulains Córdova

4

İkinci örnek yönteminin büyük olasılıkla kuplajı artırdığını iddia ediyorum , çünkü büyük olasılıkla bir Track nesnesini somutlaştırıyor ve mevcut Albüm nesnesine saklıyor. (Yukarıdaki yorumumda belirtildiği gibi, bir Album sınıfının içinde bir yerlerde Track sınıfı kavramına sahip olacağını kabul ediyorum.)

İlk örnek yöntem, bir Track'in Album sınıfının dışında başlatıldığını varsayar, bu nedenle en azından Track sınıfının örneklemesinin Album sınıfına bağlanmadığını varsayabiliriz .

En iyi uygulamalar hiçbir zaman ikinci bir sınıf referansımız olmadığını öne sürerse, nesneye yönelik programlamanın tamamı pencereden dışarı atılır.


Nasıl başka bir sınıfa örtük başvuru sahip açık bir başvuru sahip daha bağlı olduğunu görmüyorum. Her iki durumda da, iki sınıf birleştirilir. Ben kuplaj açık olması daha iyi olduğunu düşünüyorum, ama her iki şekilde de "daha fazla" birleştiğini sanmıyorum.
TMN

1
@TMN, ekstra bağlantı, ikinci örneğin muhtemelen dahili olarak yeni bir Track nesnesi oluşturacağını ima etmemle ilgilidir. Nesnenin somutlaştırılması, aksi takdirde sadece Albüm nesnesindeki bir tür listeye bir Track nesnesi eklemesi gereken bir yönteme bağlanmaktadır (Tek Sorumluluk İlkesini kırarak). Track'in oluşturulma şeklinin değiştirilmesi gerekiyorsa, addTrack () yönteminin de değiştirilmesi gerekir. İlk örnek için bu böyle değildir.
Derek

3

Kuplaj kodunuzu elde etmek için birçok açıdan sadece bir tanesidir. Kuplajı azaltarak, programınızı geliştirmeniz gerekmez. Genel olarak, bu en iyi uygulamadır, ancak bu özel örnekte neden Trackbilinmemelidir?

Aktarılacak bir Tracksınıf kullanarak Album, kodunuzun okunmasını kolaylaştırıyorsunuz, ancak daha da önemlisi, belirttiğiniz gibi, statik bir parametre listesini dinamik bir nesneye dönüştürüyorsunuz. Bu, nihayetinde arayüzünüzü daha dinamik hale getirir.

Kapsüllemenin kırıldığından bahsettiniz, ama değil. bir nesneyi Albumiçsel olarak bilmeli Trackve eğer bir nesneyi Albumkullanmadıysanız, her şeyden aynı şekilde yararlanabilmesi için ona aktarılan her bir bilgiyi bilmek zorunda kalacaksınız . TrackBir Tracknesne inşa etmesi gerektiğinden, çağıranın da içsel özelliklerini bilmesi gerekir , ancak çağıran doğrudan yönteme aktarılmışsa bu bilgiyi aynı şekilde bilmesi gerekir. Kapsülleme avantajı bir nesnenin içeriğini bilmeden eğer Başka bir deyişle, bu muhtemelen çünkü bu durumda kullanılamadı Albumait zorunluluk yapmak kullanımı Tracksadece aynı bireyin bilgi.

Eğer kullanım istemem nerede Trackolursa olduğunu Trackarayan erişmesini istemem iç mantığı içerir. Başka bir deyişle, Albumkitaplığınızı kullanan bir programcının kullanacağı bir sınıf olsaydı Track, bunu veritabanında kalmaya devam etmek için bir yöntem çağırın demek için kullanırsanız kullanmasını istemezsiniz . Bununla ilgili gerçek sorun, arayüzün modelle dolaşması gerçeğinde yatmaktadır.

Sorunu çözmek için Track, iki ayrı sınıf oluşturarak arayüz bileşenlerine ve mantık bileşenlerine ayırmanız gerekir. Arayan kişi için, Trackbilgi tutmak ve küçük optimizasyonlar (hesaplanan veriler ve / veya varsayılan değerler) sunmak amacıyla hafif bir sınıf haline gelir. İçeride Album, TrackDAObilgilerin Trackveritabanından kaydedilmesiyle ilişkili ağır kaldırmayı gerçekleştirmek için adlı bir sınıf kullanırsınız .

Tabii ki, bu sadece bir örnek. Eminim bu sizin durumunuz değildir ve bu yüzden Tracksuçsuz kullanmaktan çekinmeyin . Sadece sınıflar oluştururken arayıcınızı aklınızda tutmayı ve gerektiğinde arayüzler oluşturmayı unutmayın.


3

İkisi de doğru

addTrack( Track t ) 

olduğu daha iyi (zaten argumented gibi) ise

addTrack( int no, String title, double duration ) 

kullanan kodun bir sınıf olduğunu bilmesine gerek olmadığı için daha az bağlanır . Parça, örneğin arama kodunu güncellemeye gerek kalmadan yeniden adlandırılabilir.addTrackTrack

Daha okunabilir / bakımı kolay kodlardan bahsederken makale birleştirme hakkında konuşuyor . Daha az eşleştirilmiş kodun uygulanması ve anlaşılması her zaman daha kolay değildir.


Bakınız argüman 4; İkincisinin nasıl daha az bağlı olduğunu görmüyorum.
m3th0dman

3

Düşük Kuplaj, Kuplaj Yok anlamına gelmez . Bir şey, bir yerde, kod tabanının başka bir yerinde nesneler hakkında bilmek zorundadır ve "özel" nesnelere bağımlılığı ne kadar azaltırsanız, kodun değişmesi için daha fazla neden verirsiniz. Yazarın ikinci işlevle öne sürdüğü şey, daha az eşleştirilmiş, aynı zamanda daha az nesne yönelimlidir, bu da GRASP'nin nesneye yönelik bir tasarım yöntemi olduğu fikrine aykırıdır . Bütün mesele, sistemin bir nesne koleksiyonu ve etkileşimleri olarak nasıl tasarlanacağı; onlardan kaçınmak size bisiklet sürmeniz gerektiğini söyleyerek nasıl araba süreceğinizi öğretmek gibidir.

Bunun yerine, uygun yol, "gevşek bağlantı" teorisi olan somut nesnelere bağımlılığı azaltmaktır . Bir yöntemin ne kadar az kesin somut bilgiye sahip olması gerektiği o kadar iyidir. Sadece bu ifadeyle, ilk seçenek aslında daha az eşleşmiştir, çünkü daha basit türleri alan ikinci yöntem, bu daha basit türlerin hepsini bilmelidir. Elbette yerleşiktirler ve yöntemin içindeki kodun bakımı gerekebilir, ancak yöntemin imzası ve yöntemin arayanları kesinlikle bunu yapmaz . Kavramsal bir ses parçasına ilişkin bu parametrelerden birinin değiştirilmesi, bir Track nesnesinde (nesnelerin noktası; kapsülleme) bulundukları zamana göre ayrı olduklarında daha fazla değişiklik gerektirecektir.

Bir adım daha ileri gidecek olursak, Track'in aynı işi daha iyi yapan bir şeyle değiştirilmesi bekleniyorsa, belki de gerekli işlevselliği tanımlayan bir arabirim, bir ITrack olurdu. Bu, kavramsal olarak bir "parçayı" temsil eden ITrack'ın temel veri gösterimini sağlarken, bu formatlara daha spesifik ek bilgiler sağlayan "AnalogTrack", "CdTrack" ve "Mp3Track" gibi farklı uygulamalara izin verebilir; sonlu bir ses parçası. Track benzer şekilde soyut bir temel sınıf olabilir, ancak bu her zaman Track'de bulunan uygulamayı kullanmak istemenizi gerektirir; BetterTrack olarak yeniden uygulayın ve şimdi beklenen parametreleri değiştirmeniz gerekiyor.

Böylece altın kural; programların ve kod bileşenlerinin her zaman değiştirmek için nedenleri olacaktır. Sen edecek bir program yazamaz asla zaten yeni bir şey eklemek veya davranışını değiştirmek için yazdım düzenleme kod gerektirmez. Herhangi metodoloji Amacınız, (GRASP, KATI, başka herhangi bir kısaltma ya da terim aklınıza gelebilecek) şeyleri tanımlamak basitçe olacak zaman içinde değiştirmek zorunda ve bu değişiklikler mümkün olduğunca kolay hale getirmek için böylece sistem tasarımı (çevrilmiş; mümkün olan en az sayıda kod satırına dokunmak ve sistemin, planladığınız değişikliğin kapsamı dışında olabildiğince az sayıda başka alanını etkilemek). Değişimin en muhtemel ne Noktasında Case, bir Parça addTrack () veya yaklaşık umurumda olmayabilir o kadar çok veri üyeleri kazanacaktır olmasıdır değil bu Parça BetterTrack ile değiştirilecektir.

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.