Nesneye Dayalı Sınıf Tasarımı


12

İyi nesne yönelimli sınıf tasarımını merak ediyordum. Özellikle, bu seçenekler arasında karar vermekte zorlanıyorum:

  1. statik vs örnek yöntemi
  2. parametresiz yöntem veya dönüş değeri vs parametreli yöntem ve dönüş değeri
  3. üst üste binen karşı belirgin bir yöntem işlevselliği
  4. özel ve genel yöntem

Örnek 1:

Bu uygulama, üst üste binme işlevi olmayan dönüş değeri veya parametreleri olmayan örnek yöntemleri ve tüm yöntemleri herkese açık olarak kullanır

XmlReader reader = new XmlReader(url);
reader.openUrl();
reader.readXml();
Document result = reader.getDocument();

Örnek 2:

Bu uygulama, üst üste binen işlevsellik ve özel yöntemlerle döndürülen değerler ve parametrelerle statik yöntemler kullanır

Document result = XmlReader.readXml(url); 

Birinci örnekte, tüm yöntemler herkese açık bir örnektir ve bu da birim testini kolaylaştırır. Tüm yöntemler farklı olsa da, readXml () yöntemi, openUrl () öğesine bağımlıdır. Tüm veriler örnek alanlarında bildirilir, bu nedenle yapıcı ve erişimciler dışında hiçbir yöntemde dönüş değeri veya parametre yoktur.

İkinci örnekte, yalnızca bir yöntem herkese açıktır, geri kalanı özel statiktir ve bu da birim testini zorlaştırır. Yöntemler, readUml () çağrılarının openUrl () çağrısında çakışıyor. Alan yoktur, tüm veriler yöntemlerde parametre olarak iletilir ve sonuç hemen döndürülür.

Uygun nesne yönelimli programlama yapmak için hangi ilkeleri izlemeliyim?


3
Çok iş parçacıklı işlem yaptığınızda statik şeyler kötüdür. Geçen gün, XMLWriter.write (veri, fileurl) gibi statik bir XMLWriter vardı. Ancak, özel bir statik FileStream bulunduğundan, bu sınıfı aynı anda birden çok iş parçacığından kullanmak, ikinci iş parçacığının ilk iş parçacığı FileStream'in üzerine yazmasına neden olarak bulunması çok zor bir hataya neden olur. Statik üyelere sahip statik sınıflar + çoklu iş parçacığı felaket için bir reçetedir.
Per Alexandersson

1
@Paxinum. Açıkladığınız sorun bir durum sorunudur, "statik" bir sorun değildir. Statik olmayan üyelere sahip bir singleton kullandıysanız çoklu iş parçacığında da aynı sorunu yaşarsınız.
mike30

2
@Per Alexandersson Statik yöntemler, eşzamanlılık açısından kötü değildir. Statik durum kötü. Bu nedenle, tüm yöntemlerin statik olduğu fonksiyonel programlama, eşzamanlı durumlarda çok iyi çalışır.
Yuli Bonner

Yanıtlar:


11

Örnek 2 test için oldukça kötü ... ve içselleri test edemeyeceğiniz anlamına gelmiyorum. Hiçbir XmlReadernesneniz olmadığı için nesnenizi sahte bir nesneyle değiştiremezsiniz .

Örnek 1'in kullanımı gereksizdir. Ne dersin

XmlReader reader = new XmlReader(url);
Document result = reader.getDocument();

statik yönteminizden daha zor değildir.

URL'yi açma, XML okuma, baytları dizelere dönüştürme, ayrıştırma, yuvaları kapatma ve her ne olursa olsun ilginç değildir. Bir nesne yaratmak ve kullanmak önemlidir.

Dolayısıyla IMHO, uygun OO Tasarımı, sadece iki şeyi halka açık hale getirmektir (bir sebepten dolayı ara adımlara gerçekten ihtiyacınız yoksa). Statik kötülüktür.


-1. Aslında XmlReader bir sahte nesne ile değiştirebilirsiniz. Beyin ölü açık kaynak moick çerçevesiyle değil, ancak iyi bir endüstriyel sınıf ile yapabilirsiniz;) Geliştirici başına birkaç yüz USD maliyeti, ancak yayınladığınız API'lardaki mühürlü işlevselliği test etmek için harikalar yaratıyor.
TomTom

2
TomTom'un satış konuşmasına katılmadığı için +1. Bir astar istediğimde Document result = new XmlReader(url).getDocument();Neden? Böylece onu başka bir şeye yükseltebilir Document result = whateverReader.getDocument();ve whateverReaderbana verebilirim.
candied_orange

6

İşte bir şey - tek bir doğru cevap yok ve "uygun nesne yönelimli tasarım" ın mutlak bir tanımı yok (bazı insanlar size bir tane sunacak, ama saflar ... onlara zaman verin).

Her şey HEDEFLERİNİZE gelir.

Sen bir sanatçısın ve kağıt boş. Hassas, ince bir şekilde çizilen siyah beyaz bir yan portre veya karışık neonların büyük yaralarıyla soyut bir resim çizebilirsiniz. Veya aradaki herhangi bir şey.

Peki çözmekte olduğunuz sorun için DOĞRU HİSSEDİYOR? Xml ile çalışmak için sınıflarınızı kullanması gereken kişilerin şikayetleri nelerdir? İşleriyle ilgili zor olan nedir? Kitaplığınıza yapılan çağrıları çevreleyen ne tür bir kod yazmaya çalışıyorlar ve bu akışın onlar için daha iyi olmasına nasıl yardımcı olabilirsiniz?

Daha fazla özlülük isterler mi? Parametreler için varsayılan değerleri bulmada çok akıllı olmalarını isterler, bu yüzden fazla (veya herhangi bir şey) belirtmek zorunda kalmazlar ve doğru tahmin ederler mi? Kitaplığınızın gerektirdiği kurulum ve temizleme görevlerini, bu adımları unutmanın imkansız olması için otomatikleştirebilir misiniz? Onlar için başka ne yapabilirsiniz?

Cehennem, muhtemelen yapmanız gereken şey 4 veya 5 farklı şekilde kodlamak, sonra tüketici şapkanızı giymek ve 5'in tümünü kullanan kod yazmak ve hangisinin daha iyi hissettirdiğini görmek. Bunu tüm kitaplığınız için yapamıyorsanız, bunu bir alt küme için yapın. Listenize bazı ek alternatifler de eklemeniz gerekir - akıcı bir arayüz veya daha işlevsel bir yaklaşım veya adlandırılmış parametreler veya DynamicObject tabanlı bir şey veya onlara yardımcı olacak anlamlı "sözde yöntemler" oluşturabilmeniz için dışarı?

Neden jQuery şu anda kral? Çünkü Resig ve ekibi bu süreci, dom ve olaylarla çalışmak için gereken JS kodu miktarını inanılmaz derecede azaltan sözdizimsel bir ilkeye rastlayana kadar takip ettiler . Sözdizimsel prensip başladığında kendileri veya başkaları için net değildi. BULDULAR.

Bir programcı olarak, en yüksek çağrınız budur. Karanlıkta bir şeyler bulana kadar uğraşıyorsun. Bunu yaptığınızda bileceksiniz. Ayrıca kullanıcılarınıza büyük bir verimlilik atılımı yapacaksınız . Ve tasarım (yazılım alanında) bununla ilgilidir.


1
Bu, en iyi uygulamalar ile diğer uygulamalar arasında "nasıl hissettiğinden" başka hiçbir fark olmadığı anlamına gelir. Bu, sürdürülemeyen çamur topları ile sarılırsınız - çünkü birçok geliştiriciye, Sınıf sınırlarına uzanarak, vb.
Amy Blankenship

@Amy Blankenship, tartışmasız bir şekilde OP'nin sorduğu seçimleri yapmanın tek bir "en iyi yolu" olmadığını söyleyebilirim. Bir milyon şeye bağlıdır ve bir milyon derece özgürlük vardır. Bununla birlikte, "En İyi Uygulamalar" için bir yer olduğunu ve belirli seçimlerin zaten yapıldığı bir takım ortamında olduğunu düşünüyorum ve ekibin geri kalanına bu önceki seçimlerle tutarlı kalmak için ihtiyacımız var. Başka bir deyişle, belirli bir bağlamda , bazı şeyleri "en iyi uygulamalar" olarak etiketlemek için nedenler olabilir. Ancak OP herhangi bir bağlam vermedi. O bir şey inşa ediyor ve ...
Charlie Flowers

... tüm bu olası seçeneklerle yüzleşiyor. Bu seçeneklere tek bir "doğru cevap" yoktur. Sistemin hedefleri ve acı noktaları tarafından yönlendirilir. Haskell programcılarının tüm yöntemlerin örnek yöntem olması gerektiğini düşünmediğini garanti ederim. Ve Linux çekirdek programcıları şeyleri TDD için erişilebilir hale getirmenin hiç önemli olmadığını düşünüyorlar. Ve C ++ oyun programcıları, her şeyi nesnelere kapsüllemek yerine, verilerini bellekte sıkı bir veri yapısında birleştirmeyi tercih ederler. Her "En İyi Uygulama" belirli bir bağlamda yalnızca "en iyi uygulama" dır ve diğer bazı bağlamlarda anti-kalıptır.
Charlie Flowers

@AmyBlankenship Bir şey daha var: Sınıf sınırlarına uzanmanın “sadece harika hissettirdiğini” kabul etmiyorum. Korkunç hisseden, sürdürülemeyen çamur toplarına yol açar . Bazı işçilerin özensiz / motivasyonsuz / çok deneyimsiz olduğu sorunu çözmeye çalıştığınızı düşünüyorum. Bu durumda, dikkatli, motive ve deneyimli bir kişi anahtar seçimleri yapmalı ve onlara “En İyi Uygulamalar” demelidir. Ancak, bu "En İyi Uygulamalar" ı seçen kişi hala "doğru hissettiren" lere göre seçimler yapmaktadır ve belirlenmiş doğru cevaplar yoktur. Sadece kimin seçimler yaptığını kontrol ediyorsunuz .
Charlie Flowers

Kendilerini üst düzey olarak düşünen ve statik ve Singletonların iletişim sorunlarını ele almak için kesinlikle doğru yol olduğuna inanan yönetim tarafından bu şekilde düşünülen birkaç programcı ile çalıştım. Bu sorunun statik kısmı, Sınıf sınırlarına uzanmak gibi geliştiricilere "yanlış hissettiriyorsa" ne de statik alternatifi savunan cevabın herhangi bir oy alması istenmezdi bile.
Amy Blankenship

3

İkinci seçenek, insanların kullanması daha basit olduğu için (sadece siz bile) daha basittir.

Ünite testi için, eğer iç kısımları özelden korumalıya taşımak istiyorsanız, sadece iç kısmı değil arayüzü test ederim.


2
Test durumlarınız aynı pakette yer alıyorsa, birim sınama amacıyla yöntem paketinizi özel (varsayılan) yapabilirsiniz.

İyi bir nokta - bu daha iyi.

3

1. Statik ve örnek

Bence iyi OO tasarımı ve neyin olmadığı konusunda çok açık yönergeler var. Sorun, blogosferin iyiyi kötü ve çirkinden ayırmayı zorlaştırmasıdır. Sen bulabilirsiniz bazı aklınıza gelebilecek hatta en kötü uygulamayı kaldırdı referans tür.

Ve aklıma gelen en kötü uygulama, bahsettiğiniz statikler ve herkesin en sevdiği Singleton da dahil olmak üzere Küresel Devlettir. Misko Hevery'nin konuyla ilgili klasik makalesinden bazı alıntılar .

Bağımlılıkları gerçekten anlamak için, geliştiricilerin her kod satırını okuması gerekir. Uzakta Ürkütücü Harekete neden olur: test paketlerini çalıştırırken, bir testte mutasyona uğramış genel durum, sonraki veya paralel testin beklenmedik şekilde başarısız olmasına neden olabilir. Manuel veya Guice bağımlılığı enjeksiyonu kullanarak statik bağımlılığı kırın.

Uzaktaki Ürkütücü Eylem, izole olduğuna inandığımız bir şeyi yürüttüğümüzde (herhangi bir referans geçmediğimizden), ancak nesneye bahsetmediğimiz sistemin uzak konumlarında beklenmedik etkileşimler ve durum değişiklikleri meydana gelir. Bu sadece küresel devlet aracılığıyla olabilir.

Daha önce bu şekilde düşünmemiş olabilirsiniz, ancak statik durumu her kullandığınızda gizli iletişim kanalları oluşturuyorsunuz ve API'da netleştirmiyorsunuz. Uzaktaki Ürkütücü Eylem, geliştiricileri potansiyel etkileşimleri anlamak için her kod satırını okumaya zorlar, geliştirici verimliliğini azaltır ve yeni ekip üyelerini karıştırır.

Bunun kaygılanması, bir çeşit depolanmış durumu olan herhangi bir şeye statik referanslar vermemenizdir. Statiği kullandığım tek yer numaralandırılmış sabitler içindir ve bu konuda bile endişelerim var.

2. Giriş parametreleri ve dönüş değerleri olan yöntemler ve hiçbiri olmayan yöntemler

Fark etmeniz gereken şey, giriş parametresi ve çıkış parametresi olmayan yöntemlerin dahili olarak depolanmış bir durumda çalıştığı garantilidir (aksi halde ne yapıyorlar?). Orada tüm diller saklanan durumu engellenmiş fikri üzerine inşa edilmiştir.

Durumunuzu her kaydettiğinizde, yan etki olasılığınız vardır, bu nedenle her zaman dikkatli bir şekilde kullandığınızdan emin olun. Bu, tanımlı giriş ve / veya çıkışlara sahip işlevleri tercih etmeniz anlamına gelir .

Aslında, girişleri ve çıkışları tanımlayan işlevleri test etmek çok daha kolaydır - burada bir işlev çalıştırmanız ve ne olduğunu görmek için oraya bakmanız gerekmez ve bir yerde bir özellik ayarlamanız gerekmez test edilen fonksiyonu çalıştırmadan önce.

Bu tür işlevleri güvenli bir şekilde statik olarak da kullanabilirsiniz. Ancak, yapmazdım, çünkü daha sonra yeni uygulama ile farklı bir örnek sunmak yerine, bir yerde bu işlevin biraz farklı bir uygulamasını kullanmak istersem, işlevselliği değiştirmenin hiçbir yolu yoktu.

3. Çakışan ve Farklı

Soruyu anlamıyorum. Örtüşen 2 yöntemde avantaj ne olur?

4. Özel ve Kamu

Maruz kalmanıza gerek olmayan hiçbir şeyi ifşa etmeyin. Ancak ben de özel bir hayran değilim. C # geliştiricisi değilim, ActionScript geliştiricisiyim. Adobe'nin 2007 dolaylarında yazılan Flex Framework kodunda çok zaman geçirdim. Ve ne özel yapmak için bazı kötü seçimler yaptılar, bu da Sınıflarını genişletmeye çalışan bir kabus yapıyor.

Bu nedenle, 2007'deki Adobe geliştiricilerinden daha iyi bir mimar olduğunuzu düşünmüyorsanız (sorunuzdan, bu iddiayı yapma şansınız olmadan birkaç yılınız olduğunu söyleyebilirim), muhtemelen varsayılan olarak korunmak istersiniz .


Kod örneklerinizde iyi tasarlanmış olmadıkları anlamına gelen bazı sorunlar vardır, bu nedenle A veya B'yi seçmek mümkün değildir.

Bir kere, muhtemelen nesne yaratımınızı kullanımından ayırmalısınız . Yani genellikle new XMLReader()nerede kullanıldığının yanında hakkınız olmazdı .

Ayrıca, @djna'nın dediği gibi, XML Okuyucunuzda kullanılan yöntemleri kapsüllemelisiniz, böylece API'niz (örnek örneği) aşağıdakiler için basitleştirilebilir:

_document Document = reader.read(info);

C # nasıl çalıştığını bilmiyorum, ancak bir dizi web teknolojisi ile çalıştığımdan, her zaman hemen bir XML belgesi döndüremeyeceğinizden şüphelenirim (belki bir söz veya gelecek türü hariç) ), ancak C # 'da bir Asenkron yükün nasıl ele alınacağı konusunda size tavsiyede bulunamam.

Bu yaklaşımla, bir XML nesnesinin nerede / ne okunacağını / döndürüleceğini söyleyen bir parametre alabilen ve proje gereksinimlerinize göre bunları değiştirebileceğiniz birkaç uygulama oluşturabileceğinizi unutmayın. Örneğin, doğrudan bir veritabanından, yerel bir mağazadan veya orijinal örneğinizde olduğu gibi bir URL'den okuyor olabilirsiniz. Statik bir yöntem kullanıyorsanız bunu yapamazsınız.


2

Müşterinin bakış açısına odaklanın.

IReader reader = new XmlReader.readXml(url);  // or injection, or factory or ...
Document document = reader.read();

Statik yöntemler gelecekteki evrimi sınırlama eğilimindedir, müşterimiz muhtemelen birçok farklı uygulama tarafından sağlanan bir arayüz açısından çalışmaktadır.

Açık / okuma deyiminizle ilgili en büyük sorun, müşterinin sadece basit bir iş yapmak istediğinde yöntemleri çağırmak için sıralamayı bilmesi gerektiğidir. Burada bariz, ama daha büyük bir Sınıfta bariz olmaktan çok uzak.

Test için temel yöntem read () yöntemidir. Dahili yöntemler, programları ne genel ne de özel hale getirerek ve aynı pakette testler yaparak test etmek için görünür hale getirilebilir - testler yine de yayınlanan koddan ayrı tutulabilir.


Test takımı farklı bir projedeyse, varsayılan görünürlüğe sahip yöntemler hala görünüyor mu?
siamii

Java projeler hakkında bilgi sahibi değildir. Projeler bir IDE yapısıdır. Derleyici ve JVM, test edilen ve test eden sınıfların aynı pakette olduğu paketlere bakar, varsayılan görünüme izin verilir. Eclipse'de iki farklı kaynak dizini olan tek bir proje kullanıyorum. Sadece iki projeyle denedim, işe yarıyor.

2

statik vs örnek yöntemi

Pratikte, statik yöntemlerin genellikle yardımcı program sınıflarıyla sınırlı olduğunu ve etki alanı nesnelerinizi, yöneticilerinizi, denetleyicilerinizi veya DAO'larınızı karıştırmamanız gerektiğini göreceksiniz. Statik yöntemler, gerekli tüm referanslar makul olarak parametreler olarak iletilebildiğinde ve birçok sınıfta yeniden kullanılabilen bazı işlevlerden yararlandığında en kullanışlıdır. Kendinizi bir örnek nesneye başvuru yapmak için geçici bir çözüm olarak statik bir yöntem kullanarak bulursanız, kendinize bunun yerine neden yalnızca bu referansa sahip olmadığınızı sorun.

parametresiz yöntem veya dönüş değeri vs parametreli yöntem ve dönüş değeri

Yöntemde parametrelere ihtiyacınız yoksa bunları eklemeyin. Aynı şey dönüş değeri için de geçerlidir. Bunu akılda tutmak, kodunuzu basitleştirecek ve asla gerçekleşmeyecek çok sayıda senaryo için kod yazmamanızı sağlayacaktır.

örtüşen veya farklı yöntem işlevselliği

Çakışan işlevsellikten kaçınmaya çalışmak iyi bir fikirdir. Bazen zor olabilir, ancak mantıkta bir değişiklik gerektiğinde, yeniden kullanılan bir yöntemi değiştirmek, benzer işlevselliğe sahip bir dizi yöntemi değiştirmekten çok daha kolaydır

özel ve genel yöntem

Genelde alıcılar, belirleyiciler ve kurucular halka açık olmalıdır. Başka bir sınıfın yürütmesi gereken bir durum olmadıkça, gizli tutmaya çalışacağınız diğer her şey. Yöntemleri varsayılan olarak özel tutmak, Kapsülleme'nin korunmasına yardımcı olacaktır . Alanlar için de aynı şey geçerli, varsayılan olarak özelliğe alışmak


1

Sorunuza cevap vermeyeceğim, ancak kullandığınız terimlerle bir sorun oluştuğunu düşünüyorum. Örneğin

XmlReader.read => twice "read"

Ben bir XML ihtiyacınız olduğunu düşünüyorum böylece bir metin türünden oluşturulabilir bir nesne XML oluşturacağım (Java C # bilmiyorum ... Bu String denir). Örneğin

class XML {
    XML(String text) { [...] }
}

Test edebilirsiniz ve açıktır. Daha sonra bir fabrikaya ihtiyacınız varsa, bir fabrika yöntemi ekleyebilirsiniz (ve 2. örneğiniz gibi statik olabilir). Örneğin

class XML {
    XML(String text) { [...] }

    static XML fromUrl(url) { [...] }

}

0

Bazı basit kuralları takip edebilirsiniz. Kuralların nedenlerini anlarsanız yardımcı olur.

statik vs örnek yöntemi

Yöntemler için bu kararı bilinçli olarak vermek zorunda değilsiniz. Görünüşe göre yönteminiz herhangi bir alan üyesi kullanmıyorsa (favori analizciniz bunu söylemelidir), statik anahtar kelimeyi eklemeniz gerekir.

parametresiz yöntem veya dönüş değeri vs parametreli yöntem ve dönüş değeri

İkinci seçenek kapsam nedeniyle daha iyidir. Kapsamınızı her zaman sıkı tutmalısınız. İhtiyacınız olan şeylere ulaşmak kötü, girdiniz olmalı, yerel olarak çalışmalı ve bir sonuç getirmelisiniz. İlk seçeneğiniz aynı nedenden dolayı kötüdür, global değişkenler genellikle kötüdür: kodunuzun sadece bir kısmı için anlamlıdırlar, ancak başka her yerde görünürler (ve böylece gürültü çıkarırlar) ve herhangi bir yerden kurcalanabilirler. Bu, mantığınızın tam bir resmini elde etmeyi zorlaştırır.

örtüşen veya farklı yöntem işlevselliği

Bunun bir sorun olduğunu düşünmüyorum. Diğer yöntemleri çağıran yöntemler, görevleri daha küçük belirgin işlevsel parçalara ayırırsa iyidir.

özel ve genel yöntem

Herkese açık olmanız gerekmedikçe her şeyi özel yapın. Sınıfınızın kullanıcısı gürültü yapmadan yapabilir, sadece onun için önemli olan şeyleri görmek ister.

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.