Bireysel özellikler yerine bütün sınıfları parametre olarak alabilmek için bir Sınıf tasarlamak


30

Diyelim ki, denilen yaygın olarak paylaşılan bir sınıfla bir uygulamanız var User. Bu sınıf, kullanıcı, kimliği, adı, her modüle erişim düzeyleri, saat dilimi vb. Hakkındaki tüm bilgileri gösterir.

Kullanıcı verilerine açıkça sistem genelinde geniş bir şekilde atıfta bulunulmaktadır, ancak her ne sebeple olursa olsun, sistem bu kullanıcı nesnesini ona bağlı sınıflara aktarmak yerine, sadece ondan ayrı özelliklere sokacağımız şekilde kurulur.

Kullanıcı kimliğini gerektiren bir sınıf, GUID'yi userIdbir parametre olarak basitçe gerektirir , bazen ayrı bir parametre olarak iletilmek üzere kullanıcı adına da ihtiyacımız olabilir. Bazı durumlarda, bu bireysel yöntemlere iletilir, bu nedenle değerler sınıf düzeyinde hiç tutulmaz.

Her seferinde, Kullanıcı sınıfından farklı bir bilgiye erişmem gerektiğinde, parametreler ekleyerek değişiklikler yapmalıyım ve yeni bir aşırı yük eklemenin uygun olmadığı yerlerde, metoda veya sınıf yapıcısına her başvuruyu da değiştirmeliyim.

Kullanıcı sadece bir örnek. Bu kodumuzda geniş çapta uygulanmaktadır.

Bunun Açık / Kapalı prensibinin ihlali olduğunu düşünmekte haklı mıyım? Sadece var olan sınıfları değiştirme eylemi değil, aynı zamanda ilk etapta onları kurmak, böylece yaygın değişikliklerin gelecekte yapılması çok muhtemeldir?

UserNesneye yeni geçersek, birlikte çalıştığım sınıfta küçük bir değişiklik yapabilirim. Bir parametre eklemem gerekirse, sınıfa yapılan referanslarda onlarca değişiklik yapmak zorunda kalabilirim.

Bu uygulama tarafından çiğnenmiş başka ilkeler var mı? Belki de bağımlılık inversiyonu? Bir soyutlamaya atıfta bulunmasak da, sadece bir tür kullanıcı var, bu yüzden bir Kullanıcı arayüzüne sahip olmak için gerçek bir ihtiyaç yok.

Temel savunma programlama ilkeleri gibi, diğer katı olmayan ilkeler ihlal edildi mi?

Yapıcım şöyle görünmeli:

MyConstructor(GUID userid, String username)

Veya bu:

MyConstructor(User theUser)

Onu gönderdi:

Sorunun "Pass ID or Object?" İçinde cevaplanması önerildi. Bu, her iki yoldan gitme kararının, bu sorunun özünde olan SOLID ilkelerini takip etme girişimini nasıl etkilediği sorusuna cevap vermez.


11
@gnat: Bu kesinlikle bir kopya değil. Olası kopya, bir nesne hiyerarşisine derinlemesine ulaşmak için yöntem zincirlemesidir. Bu soru hiç sormuyor gibi görünüyor.
Greg Burghardt

2
İkinci form genellikle geçirilen parametrelerin sayısı bozulmadığında kullanılır.
Robert Harvey,

12
İlk imzadan hoşlanmadığım tek şey, kullanıcı kimliğinin ve kullanıcı adının aslında aynı kullanıcıdan geldiğine dair hiçbir garanti olmamasıdır. Her yerden Kullanıcı'nın etrafından dolaşarak kaçınılması muhtemel bir hatadır. Ancak karar gerçekten de adlandırılan yöntemlerin argümanlarla ne yaptığına bağlıdır.
26

9
“Ayrıştır” kelimesi, kullandığınız bağlamda bir anlam ifade etmiyor. Bunun yerine “geçmek” mi demek istediniz?
Konrad Rudolph

5
Peki ya Iiçeri SOLID? MyConstructortemelde şimdi "bir Guidve bir ihtiyacım var" diyor string. Öyleyse neden bir sağlayan bir arayüze sahip değildir Guidve bir string, let Usero arabirimini uygulayan ve let MyConstructoro arayüzü uygulayan bir örneği bağlıdır? Ve eğer MyConstructordeğişim ihtiyaçları varsa , arayüzü değiştirin. - Sağlayıcıdan ziyade , tüketiciye “ait” arayüzleri düşünmek için bana çok yardımcı oldu . Öyleyse, "bir tüketici olarak bunu yapan ve bunu yapan bir şeye ihtiyacım var" yerine " sağlayıcı olarak bunu ve bunu yapabilirim " düşünün .
Corak

Yanıtlar:


31

Bütün bir Usernesneyi parametre olarak geçirirken kesinlikle yanlış bir şey yoktur . Aslında, kodunuzu netleştirmeye yardımcı olabilir ve yöntem imzası gerektiriyorsa bir yöntemin ne yapması gerektiğini programcılar için daha açık hale getirebilir User.

Basit veri türlerini iletmek, oldukları şeyden başka bir şey ifade edene kadar güzeldir. Bu örneği düşünün:

public class Foo
{
    public void Bar(int userId)
    {
        // ...
    }
}

Ve bir örnek kullanım:

var user = blogPostRepository.Find(32);
var foo = new Foo();

foo.Bar(user.Id);

Kusuru tespit edebilir misiniz? Derleyici olamaz. Geçirilen "kullanıcı kimliği" sadece bir tamsayıdır. Değişkeni adlandırırız, userancak değerini blogPostRepositorynesneden değil, muhtemelen BlogPostnesneleri döndüren nesneden başlatırız, ancak Userkod derlenir ve siz de haksız bir çalışma zamanı hatası ile sonuçlanırsınız.

Şimdi bu değiştirilmiş örneği ele alalım:

public class Foo
{
    public void Bar(User user)
    {
        // ...
    }
}

Belki de Baryöntem yalnızca "kullanıcı kimliğini" kullanır, ancak yöntem imzası bir Usernesne gerektirir . Şimdi eskisi gibi aynı örnek kullanıma geri dönelim, ancak içindeki "kullanıcı" nın tamamını geçecek şekilde değiştirin:

var user = blogPostRepository.Find(32);
var foo = new Foo();

foo.Bar(user);

Şimdi bir derleyici hatamız var. Bu blogPostRepository.Findyöntem BlogPostakıllıca "user" olarak adlandırdığımız bir nesne döndürür . Daha sonra bu "kullanıcıyı" Barmetoda iletiriz ve derhal bir derleyici hatası alırız, çünkü BlogPosta'yı kabul eden bir metoda geçiremeyiz User.

Dilin Tip Sistemi, doğru kodu daha hızlı yazmak ve çalışma zamanı yerine derleme zamanında hataları tanımlamak için kullanılır.

Gerçekten de, kullanıcı bilgileri değiştiğinden çok sayıda kodu yeniden gözden geçirmek zorunda kalmak sadece başka sorunların belirtisidir. Bütün bir Usernesneyi geçerek , Usersınıfla ilgili bir şey değiştiğinde kullanıcı bilgisini kabul eden tüm yöntem imzalarını yeniden incelemek zorunda olmamanın yararlarına ek olarak, yukarıdaki faydaları elde edersiniz .


6
Mantığınızın kendiliğinden aslında geçiş alanlarına işaret ettiğini ancak alanların gerçek değer etrafında önemsiz sarmalayıcılar olduğunu söyleyebilirim. Bu örnekte, bir Kullanıcı Kullanıcı Kimliği türünde bir alana sahiptir ve Kullanıcı Kimliği tek bir tamsayı değerli alana sahiptir. Şimdi Bar ilanı size derhal Bar'ın Kullanıcı ile ilgili tüm bilgileri kullanmadığını, sadece onların kimliğini kullanmadığını söyler, ancak yine de bir Kullanıcı Kimliği'nden Bar'a gelmeyen bir tamsayıyı geçmek gibi aptalca hatalar yapamazsınız.
Ian

(Devam) Elbette bu tür bir programlama stili oldukça sıkıcıdır, özellikle bunun için sözdizimsel desteği olmayan bir dilde (Haskell, bu stil için güzel, örneğin "Kullanıcı Kimliği" ile eşleştirebileceğiniz için) .
Ian

5
@Ian: Bir kimliği kendi türünde silmenin OP'nin ortaya çıkardığı asıl mesele etrafında kaydığını düşünüyorum; bu, Kullanıcı sınıfında yapısal değişiklikler olması birçok yöntem imzasını yeniden yapılandırmayı gerekli kılıyor. Tüm User nesnesini geçmek bu sorunu çözer.
Greg Burghardt,

@Ian: Dürüst olmamıza rağmen, C # 'da bile çalışıyorum. Sadece biraz daha fazla netlik vermek için İd'leri ve bir Cümle türlerini sarmalamaya çok hevesliyim.
Greg Burghardt,

1
"Etrafına bir işaretçi koymakla yanlış bir şey yok." Ya da bir referans, karşılaşabileceğiniz işaretçilerle ilgili tüm sorunlardan kaçınmak için.
Yay295

17

Bunun Açık / Kapalı prensibinin ihlali olduğunu düşünmekte haklı mıyım?

Hayır, bu ilkenin ihlali değil. Bu ilke, Useronu kullanan kodun diğer kısımlarını etkileyen şekillerde değişiklik yapmama ile ilgilidir . Değişiklikleriniz Userböyle bir ihlal olabilir, ancak ilgisi yoktur.

Bu uygulama tarafından çiğnenmiş başka ilkeler var mı? Bağımlılık inversiyonu belki?

Hayır. Tanımladığınız şey - bir kullanıcı nesnesinin yalnızca gerekli kısımlarını her bir yönteme enjekte etmek - bunun tam tersi: saf bağımlılık inversiyonu.

Temel savunma programlama ilkeleri gibi, diğer katı olmayan ilkeler ihlal edildi mi?

Hayır. Bu yaklaşım mükemmel bir şekilde kodlama yöntemidir. Bu ilkeleri ihlal etmiyor.

Ancak bağımlılık inversiyonu sadece bir ilkedir; kırılmaz bir yasa değil. Ve saf DI sisteme karmaşıklık katabilir. Tüm kullanıcı nesnesini yönteme veya yapıcıya aktarmak yerine yalnızca gerekli kullanıcı değerlerini yöntemlere enjekte etmenin sorun yarattığını tespit ederseniz, o zaman bunu yapmayın. Her şey prensiplerle pragmatizm arasında bir denge kurmakla ilgili.

Yorumunuzu ele almak için:

Gereksiz yere zincirin beş seviyesinden aşağıya yeni bir değer ayrıştırma ve ardından tüm referansları mevcut yöntemlerin beşine de değiştirme zorunluluğu var ...

Buradaki meselenin bir kısmı, "gereksiz yere [geçiş] ..." yorumuna göre, bu yaklaşımı açıkça beğenmemeniz. Ve bu yeterince adil; Burada doğru cevap yok. Eğer ağır bulursanız o zaman böyle yapmayın.

Bununla birlikte, açık / kapalı ilke ile ilgili olarak, “o zaman bu mevcut yöntemlerin beşine yapılan tüm referansları değiştir ...” ifadesini kesinlikle uygularsanız, bu yöntemlerin ne zaman yapılması gerektiğine dair bir göstergedir. modifikasyona kapalı. Gerçekte, açık / kapalı prensibi halka açık API'ler için iyi anlam ifade eder, ancak bir uygulamanın iç kullanıcıları için pek bir anlam ifade etmez.

... ama elbette, uygulanabilir olduğu sürece, bu ilkeye uymayı planlamanız, gelecekteki değişime duyulan ihtiyacı azaltmak için stratejiler içerir mi?

Ama sonra YAGNI topraklarına girersin ve hala ilkeye dik olur. FooBir kullanıcı adı alan bir yönteme sahipseniz ve o zaman Foodoğum tarihini de almak istiyorsanız , ilkeyi izleyerek yeni bir yöntem ekleyin; Foodeğişmeden kalır. Yine, genel API'ler için iyi bir uygulamadır, ancak dahili kod için saçmalıktır.

Daha önce belirtildiği gibi, verilen herhangi bir durum için denge ve sağduyu ile ilgilidir. Bu parametreler sıklıkla değişiyorsa, evet, Userdoğrudan kullanın . Sizi tarif ettiğiniz büyük çaplı değişikliklerden kurtaracak. Fakat sık sık değişmezse, o zaman sadece ihtiyaç duyulanı geçmek de iyi bir yaklaşımdır.


Gereksiz yere zincirin beş seviyesinden aşağıya yeni bir değer ayrıştırma ve ardından tüm referansları mevcut yöntemlerin beşine de değiştirme zorunluluğu var. Açık / Kapalı ilkesi neden diğer sınıflar tarafından da tüketilen, şu anda düzenlemekte olduğum sınıfa değil, yalnızca Kullanıcı sınıfına uygulanır? İlkenin özellikle değişiklikten kaçınmakla ilgili olduğunun farkındayım, ancak kesinlikle uygulanabilir olduğu sürece bu ilkeye uyma planının gelecekteki değişime duyulan ihtiyacı azaltma stratejilerini içerecektir?
Jimbo

@Jimbo, yorumunuzu denemek ve ele almak için cevabımı güncelledim.
David Arno

Katkınız için teşekkür ederim. BTW. Robert C Martin bile Açık / Kapalı prensibinin katı bir kuralı olduğunu kabul etmez. Bu kaçınılmaz olarak kırılacak bir kuraldır. İlkenin uygulanması, uygulanabilir olduğu kadar ona uymaya çalışmak için bir egzersizdir. Bu yüzden daha önce "uygulanabilir" kelimesini kullandım.
Jimbo

Kullanıcı'nın parametrelerini değil, Kullanıcı'nın parametrelerini iletmek bağımlılık inversiyonu değildir.
James Ellis-Jones

@ JamesEllis-Jones, Bağımlılık evrimi bağımlılıkları "sormak", "anlatmak" için çeviriyor. Bir Userörneği iletir ve sonra bir parametreyi almak için bu nesneyi sorgularsanız, bağımlılıkları yalnızca kısmen tersine çevirirsiniz; Hala devam eden bir şeyler var. Gerçek bağımlılık inversiyonu% 100 “söyle, sorma” dır. Ancak bu karmaşık bir bedelle gelir.
David Arno,

10

Evet, mevcut bir işlevi değiştirmek Açık / Kapalı Prensibi ihlal ediyor. Gereksinim değişikliği nedeniyle değişime kapalı olması gereken bir şeyi değiştiriyorsunuz. Daha iyi bir tasarım (gereksinimler değiştiğinde değişmemek), Kullanıcıyı kullanıcılar üzerinde çalışması gereken şeylere aktarmak olacaktır .

Eğer boyunca geçen olabilir çünkü Ama bu, Arayüz Ayrışma İlkesi uyulmadığını olabilir şekilde işini yapmak için fonksiyon ihtiyacı daha fazla bilgi.

Yani, çoğu şeyde olduğu gibi - buna bağlı .

Sadece bir kullanıcı adı kullanarak, fonksiyonun daha esnek olalım, nereden geldiklerinden bağımsız olarak kullanıcı isimleriyle çalışarak tam olarak çalışan bir Kullanıcı nesnesi oluşturmaya gerek kalmadan çalışalım. Veri kaynağının değişeceğini düşünüyorsanız, değişme esnekliği sağlar.

Kullanıcının tamamını kullanmak kullanımı daha açık hale getirir ve arayanlarla daha sıkı bir sözleşme yapar. Kullanıcının daha fazlasına ihtiyaç duyulacağını düşünüyorsanız, değişiklik yapma esnekliği sağlar.


+1, ancak "daha fazla bilgi iletiyor olabilirsiniz" ifadenizden emin değilim. Geçtiğin zaman (Kullanıcı kullanıcısı), bir nesneye referans olarak en az bilgiyi iletirsiniz. Bu referansın daha fazla bilgi almak için kullanılabileceği doğru, ancak arama kodunun bunu almak zorunda olmadığı anlamına gelir. Geçtiğinde (GUID kullanıcı kimliği, dize kullanıcı adı) çağrılan yöntem, nesnenin ortak arabirimini bulmak için her zaman User.find (kullanıcı kimliği) öğesini çağırabilir, böylece gerçekten hiçbir şey gizlemezsiniz.
18'de

5
@dcorking, "Geçtiğinde (Kullanıcı kullanıcısı) en az bilgiyi, bir nesneye referansı geçersin ". Bu nesneyle ilgili maksimum bilgiyi iletirsiniz: bütün nesne. " çağrılan yöntem her zaman User.find (userid) ... 'ı çağırabilir ." İyi tasarlanmış bir sistemde, söz konusu yöntemin erişemeyeceği için bu mümkün olmazdı User.find(). Aslında orada bile olmamalıdır olmak bir User.find. Bir kullanıcı bulmak asla sorumluluğunda olmamalıdır User.
David Arno

2
@dcorking - enh. Küçük görünen bir referansı geçmeniz teknik tesadüflerdir. Userİşlevin tamamını birleştiriyorsun . Belki bu mantıklı. Fakat belki de işlev, yalnızca kullanıcının adını önemsemelidir - ve Kullanıcının birleşme tarihi veya adresi gibi şeyleri iletmek uygun değildir.
Telastyn

@DavidArno, OP'ye açık bir cevap vermenin anahtarıdır. Bir kullanıcıyı bulmak kimin sorumluluğunda olmalı? Bulucu / fabrikayı sınıftan ayırmanın tasarım prensibi için bir isim var mı?
Haziran’da

1
@Dcorking, Tek Sorumluluk İlkesinin bir örneği olduğunu söyleyebilirim. Kullanıcıların nerede saklandıklarını ve kimlikleriyle nasıl alınacaklarını bilmek, bir Usersınıfın sahip olmaması gereken ayrı sorumluluklardır . UserRepositoryBu tür şeylerle ilgilenen bir veya benzeri olabilir .
Hulk

3

Bu tasarım Parametre Nesne Şablonunu izler . Yöntem imzasında birçok parametrenin bulunmasından kaynaklanan problemleri çözer.

Bunun Açık / Kapalı prensibinin ihlali olduğunu düşünmekte haklı mıyım?

Hayır. Bu kalıbı uygulamak Aç / Kapat ilkesini (OCP) mümkün kılar . Örneğin User, tüketici sınıfında farklı bir davranışa neden olan türev sınıfları parametre olarak sağlanabilir.

Bu uygulama tarafından çiğnenmiş başka ilkeler var mı?

O yapabilirsiniz olur. SOLID ilkeleri temelinde açıklamama izin verin.

Tek sorumluluk ilkesi Eğer açıkladığımız gibi tasarıma sahip olup olmadığını (SRP) ihlal edilebilir:

Bu sınıf, kullanıcı, kimliği, adı, her modüle erişim düzeyleri, saat dilimi vb. Hakkındaki tüm bilgileri gösterir.

Sorun tüm bilgilerle ilgili . Eğer Usersınıf birçok özelliğe sahipse, ilgisiz sınıfları tüketen sınıflar perspektifinden taşıyan büyük bir Veri Aktarım Nesnesi olur . Örnek: Tüketici bir sınıf perspektifinden bakıldığında UserAuthentication, mülk User.Idve User.Namekonuyla ilgilidir, ancak değildir User.Timezone.

Arayüz segregasyon prensibi (ISS) ayrıca benzer akıl ile ihlal ama başka bir bakış açısı ekler. Örnek: Tüketici bir sınıfın UserManagementözelliğin User.Namebölünmesini gerektirdiğini User.LastNameve User.FirstNamebunun UserAuthenticationiçin de sınıfın değiştirilmesi gerektiğini varsayalım .

Neyse ki ISS ayrıca size problemden kurtulmanın olası bir yolunu sunar: Genellikle bu Parametre Nesneleri veya Veri Aktarım Nesneleri küçük başlar ve zamanla büyür. Bu hantallaşırsa aşağıdaki yaklaşımı göz önünde bulundurun: Tüketici sınıfların ihtiyaçlarına göre uyarlanmış arayüzler tanıtın. Örnek: Arabirimleri tanıtın ve Usersınıfın ondan türetmesine izin verin :

class User : IUserAuthenticationInfo, IUserLocationInfo { ... }

Her arayüz, Usertüketen bir sınıfın çalışmasını tamamlamak için gerekli olan sınıfın ilgili özelliklerinin bir alt kümesini göstermelidir . Özellik kümelerini arayın. Arayüzleri tekrar kullanmayı deneyin. Tüketici sınıfı durumunda yerine UserAuthenticationkullanın . Daha sonra eğer mümkünse , arayüzleri "şablon" olarak kullanarak, sınıfı çoklu beton sınıflarına ayırın.IUserAuthenticationInfoUserUser


1
Kullanıcı karmaşıklaştıktan sonra, olası alt ara yüzlerin birleşimsel patlaması var; örneğin, Kullanıcı sadece 3 özelliğe sahipse, 7 olası kombinasyon var. Teklifin kulağa hoş geliyor ama işe yaramıyor.
user949300,

1. Analitik olarak haklısınız. Ancak, alanın nasıl modellendiğine bağlı olarak, ilgili bilgilerin parçaları kümelenme eğilimindedir. Bu nedenle, pratik olarak, tüm arayüz ve özellik kombinasyonlarını ele almak gerekli değildir. 2. Özetlenen yaklaşımın evrensel bir çözüm olması amaçlanmamıştır, ancak belki de cevaba biraz daha 'mümkün' ve 'olabilir' eklemeliyim.
Theo Lenndorff

2

Bu sorunla kendi kodumda karşılaştığımda, temel model sınıflarının / nesnelerinin cevap olduğu sonucuna vardım.

Yaygın bir örnek depo modeli olacaktır. Genellikle bir veri tabanı depoları aracılığıyla sorgulanırken, veri havuzundaki yöntemlerin çoğu aynı parametrelerin çoğunu alır.

Havuzlar için temel kurallarım:

  • Birden fazla yöntemin aynı 2 veya daha fazla parametre aldığı durumlarda, parametreler bir model nesnesi olarak birlikte gruplandırılmalıdır.

  • Bir yöntemin 2'den fazla parametre aldığı durumlarda, parametreler model nesne olarak gruplandırılmalıdır.

  • Modeller ortak bir temelden miras alabilir, ancak yalnızca gerçekten mantıklı olduğu zaman (genellikle akılda kalıtım ile başlamasından daha sonra refaktör için daha iyidir).


Başka katmanlardan / alanlardan modellerin kullanımı ile ilgili problemler, proje biraz karmaşık hale gelinceye kadar belirginleşmiyor. Ancak o zaman daha az kod bulmanız daha fazla iş veya daha fazla komplikasyon yaratır.

Ve evet, farklı katmanlara / amaçlara hizmet eden aynı özelliklere sahip 2 farklı modele sahip olmak tamamen iyidir (örn. ViewModels vs POCOs).


2

SOLID'in bireysel yönlerini kontrol edelim:

  • Tek sorumluluk: eğer insanlar sınıfın sadece bölümlerini geçme eğilimindeyse, büyük olasılıkla sümüklüdürler.
  • Açık / kapalı: Sınıfın bölümlerinin nerede geçtiği ile ilgili değil, sadece tam nesnenin geçtiği yer ile ilgili. (Sanırım bilişsel uyumsuzluğun başladığı yer: uzak kodları değiştirmeniz gerekiyor ama sınıfın kendisi iyi görünüyor.)
  • Liskov ikame: Sorun değil, alt sınıfları yapmıyoruz.
  • Bağımlılık inversiyonu (soyutlamalara bağlıdır, somut verilere değil). Evet, ihlal edildi: İnsanların soyutlamaları yok, sınıfın somut unsurlarını alıp etrafa geçiriyorlar. Bence buradaki asıl mesele bu.

Tasarım içgüdülerinin kafasını karıştırmaya meyilli olan şeylerden biri, sınıfın esasen küresel nesneler ve esasen salt okunur olması. Böyle bir durumda, soyutlamaları ihlal etmek çok fazla acı vermez: Sadece değiştirilmemiş verileri okumak oldukça zayıf bir eşleşme yaratır; sadece çok büyük bir yığın haline geldiğinde ağrı belirginleşir.
Tasarım içgüdülerinin eski haline getirilmesi için, nesnenin çok küresel olmadığını varsayınız. Bir işlevin hangi içeriğe ihtiyacı varsaUser nesnenin herhangi bir zamanda mutasyona uğratılması , hangi olabilir? Nesnenin hangi bileşenleri muhtemelen birlikte mutasyona uğrar? Bunlar, Userbaşvurulan bir alt nesne veya ilgili alanların sadece bir "dilimini" ortaya çıkaran bir arabirim olarak ayrılabilir .

Başka bir prensip: Bir kısmını kullanan Userve hangi alanların (niteliklerin) bir araya gelme eğiliminde olduğunu gören fonksiyonlara bakın. Bu, iyi bir alt hedefler listesi - onların aslında bir araya gelip gelmediklerini kesinlikle düşünmeniz gerekir.

Çok fazla iş ve yapılması biraz zor ve kodunuz biraz daha az esnek hale gelecektir, çünkü özellikle alt nesneler çakışıyorsa, bir işleve iletilmesi gereken alt nesneyi (alt arabirim) tanımlamak biraz zorlaşacaktır.

Alt Usernesneler çakışırsa, ayrılma gerçekten çirkinleşir, sonra gerekli alanların üst üste gelmesi durumunda hangisinin seçileceği konusunda insanlar karışır. Hiyerarşik olarak ayrılırsanız (örneğin UserMarketSegment, diğer şeylerin yanı sıra, sahip olduğunuz UserLocation), insanlar yazdıkları işlevin hangi düzeyde olduğundan emin değillerdir: kullanıcı Location düzeyinde ya da kullanıcı düzeyindeMarketSegment ? Bunun zaman içinde değişmesine tam olarak yardım etmiyor, yani bazen bütün bir çağrı zincirinde fonksiyon imzalarını değiştirmeye geri dönüyorsunuz.

Başka bir deyişle: Etki alanınızı gerçekten tanımadığınız ve hangi modülün hangi yönleriyle ilgilendiği konusunda net bir fikriniz Useryoksa, programın yapısını iyileştirmeye değmez.


1

Bu gerçekten ilginç bir soru. Bağlıdır.

Gelecekte, metodunuzun Kullanıcı nesnesinin farklı parametrelerini gerektirecek şekilde değişebileceğini düşünüyorsanız, kesinlikle her şeyi yapmalısınız. Bunun avantajı, yöntemin dışında kalan kodun, hangi parametreleri kullandığı, yöntemde yapılan değişikliklerden korunmalarını sağlamaktır. Böylece tüm Kullanıcıya geçmek enkapsülasyonu arttırır.

Kullanıcının e-postasını söylemekten başka bir şey kullanmanıza gerek kalmayacağından eminseniz, bunu iletmelisiniz. Bunun avantajı, yöntemi daha geniş bir bağlam aralığında kullanabilmenizdir: örneğin, Şirket e-postasıyla veya yeni girilen bir e-posta ile. Bu esnekliği artırır.

Bu, sınıfları oluşturma konusunda, bağımlılıkların enjekte edilip edilmemesi ve küresel olarak kullanılabilir nesnelerin bulunup bulunmadığı da dahil olmak üzere geniş veya dar bir kapsamı olan daha geniş bir yelpazedeki soruların bir parçasıdır. Şu anda daha dar kapsamın her zaman iyi olduğunu düşünmek için talihsiz bir eğilim var. Bununla birlikte, kapsülleme ve bu durumda olduğu gibi esneklik arasında her zaman bir sapma vardır.


1

Mümkün olduğunca az ve mümkün olduğunca az parametre iletmenin en iyisi olduğunu düşünüyorum. Bu, testi kolaylaştırır ve tüm nesnelerin kafeslenmesini gerektirmez.

Örnekte, yalnızca kullanıcı kimliğini veya kullanıcı adını kullanacaksanız, geçirmeniz gereken tek şey budur. Bu desen birkaç kez tekrarlanırsa ve gerçek kullanıcı nesnesi çok daha büyükse, tavsiyem bunun için daha küçük bir arayüz (ler) oluşturmaktır. Olabilir

interface IIdentifieable
{
    Guid ID { get; }
}

veya

interface INameable
{
    string Name { get; }
}

Bu, alay etmeyi test etmeyi çok kolaylaştırır ve anında hangi değerlerin kullanıldığını bilirsiniz. Aksi halde, çoğu zaman bir veya iki özelliğe ihtiyaç duymanıza rağmen, çoğu zaman diğer birçok bağımlılıkla karmaşık nesneleri atlatmanız gerekir.


1

İşte zaman zaman karşılaştığım bir şey:

  • Bir yöntem türü bir argüman alır UserProduct yalnızca bir kaçını kullanmasına rağmen, birçok özelliği olan bir (veya veya her neyse) alır.
  • Bazı nedenlerden dolayı, kodun bir kısmı tam olarak doldurulmamış olsa bile bu yöntemi çağırması gerekir. User nesneye . Bir örnek yaratır ve yalnızca yöntemin gerçekten ihtiyaç duyduğu özellikleri başlatır.
  • Bu birkaç kez olur.
  • Bir süre sonra, Userargümanı olan bir yöntemle karşılaştığınızda , Userhangi özelliklerin doldurulduğunu bilmeniz için nereden geldiğini bulmak için bu yönteme yapılan çağrıları bulmak zorundasınız . Bir e-posta adresine sahip "gerçek" bir kullanıcı mı, yoksa sadece bir kullanıcı kimliği ve bazı izinleri geçmek için mi oluşturuldu?

Bir Userve sadece birkaç özellik doldurursanız, yöntemin ihtiyaç duyduğu özelliklerden dolayı bunlar kullanırsanız, arayan kişi yöntemin iç işleri hakkında gerçekten gerekenden daha fazlasını bilir.

Ne zaman da kötüsü, sahip bir örneğini User, bunu sen özellikleri doldurulur hangi biliyoruz ki nereden geldiğini bilmek zorunda. Bunu bilmek zorunda olmak istemezsin.

Zaman içinde, geliştiriciler Useryöntem bağımsız değişkenleri için bir kapsayıcı olarak kullanıldığında, tek kullanımlık senaryolar için özellikler eklemeye başlayabilirler. Şimdi çirkinleşiyor, çünkü sınıf neredeyse her zaman boş ya da varsayılan olacak özelliklerle karışıklaşıyor.

Bu tür bir yolsuzluk kaçınılmaz değildir, ancak etrafından bir nesneyi geçtiğimizde tekrar tekrar ortaya çıkar, çünkü özelliklerinin bir kısmına erişmemiz gerekir. Tehlikeli bölge birisini ilk kez yaratırken Userve birkaç özelliği doldurarak ilk kez görüyorsunuz, böylece onu bir yönteme geçirebilirler. Ayağını yere koy çünkü karanlık bir yol.

Mümkünse, bir sonraki geliştirici için doğru örneği yalnızca geçmeniz gerekenleri ileterek ayarlayın.

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.