Her şey için tür tanımlamalı mıyız?


141

Son zamanlarda kodumun okunabilirliği ile ilgili bir sorunla karşılaştım.
Bir işlem yapan ve ileride başvurmak üzere bu işlemin kimliğini temsil eden bir dize döndüren bir işlevim vardı (Windows'ta bir tanıtıcı döndüren biraz OpenFile gibi). Kullanıcı bu kimliği işlemi daha sonra başlatmak ve sonunu izlemek için kullanır.

ID birlikte çalışabilirlik endişeleri nedeniyle rastgele bir dize olmalıydı. Bu, şöyle net olmayan bir imzanın olduğu bir yöntem yarattı:

public string CreateNewThing()

Bu, geri dönüş tipinin amacını belirsiz hale getirir. Bu dizgiyi, anlamını şöyle netleştiren başka bir türe sarmayı düşündüm:

public OperationIdentifier CreateNewThing()

Tür yalnızca dize içerecek ve bu dize kullanıldığında kullanılacaktır.

Bu çalışma şeklinin avantajının daha fazla güvenlik ve daha açık bir niyet olduğu açıktır, ancak aynı zamanda çok salak olmayan bir çok daha fazla kod ve kod yaratmaktadır. Bir yandan ek güvenliği seviyorum ama aynı zamanda çok fazla dağınıklık yaratıyor.

Güvenlik nedenleriyle basit türleri bir sınıfa sarmanın iyi bir uygulama olduğunu düşünüyor musunuz?


4
Minik Türler hakkında okumak isteyebilirsiniz. Bir süre onunla oynadım ve bunu tavsiye etmem ama bunun hakkında düşünmek eğlenceli bir yaklaşım. darrenhobbs.com/2007/04/11/tiny-types
Jeroen Vannevel

40
Haskell gibi bir şey kullandığınızda, türleri kullanmanın ne kadar yardımcı olduğunu göreceksiniz. Türler doğru bir programın sağlanmasına yardımcı olur.
Nate Symer,

3
Erlang gibi dinamik bir dil bile bir tür sistemden faydalanır, bu nedenle dinamik bir dil olmasına rağmen Haskellers'e aşina bir typepec sistemi ve yazım denetleme aracı vardır. Gerçek bir türün, belirli bir yöntemi ele almak için derleyiciyle aynı fikirde olduğumuz bir tamsayı olduğu C / C ++ gibi diller ya da sınıfları semantik aşırı yükleme problemi olan türler gibi davranmaya karar verirseniz, hemen hemen sortabileceğiniz türler. dil (bu bir "sınıf" mı, yoksa "tür" mü?) veya tür belirsizliği (bu "URL", "Dize" veya "UPC" mi?). Sonunda, küçük düşürmek için elinizden gelen araçları kullanın.
zxq9

7
Bu fikrin nereden geldiğine emin değilim, C ++ 'nın bir tür ek yükü vardır. C ++ 11 ile derleyici yapanların hiçbiri her lambda'ya kendi benzersiz tipini vermede bir sorun görmedi. Ancak TBH, "C / C ++ tip sistem" hakkında bir yorumda, sanki ikisi bir tip sistemini paylaşıyormuş gibi, çok fazla bilgi edinmeyi bekleyemezsiniz.
MSalters

2
@JDB - hayır, değil. Benzer bile değil.
Davor Ždralo

Yanıtlar:


152

Bir işletme alanında, stringveya gibi ilkellerin inthiçbir anlamı yoktur. Bunun doğrudan bir sonucu, bir ürün kimliği beklendiğinde yanlışlıkla bir URL kullanabilir veya fiyat beklerken miktar kullanabilirsiniz .

Nesne Calisthenics'in mücadelesinin kurallarından biri olan ilkelleri içermesinin nedeni de budur :

Kural 3: Tüm ilkelleri ve dizeleri kaydır

Java dilinde, int bir ilkeldir, gerçek bir nesne değildir, bu yüzden nesnelerden farklı kurallara uyar. Nesne yönelimli olmayan bir sözdizimi ile kullanılır. Daha da önemlisi, kendi başına bir int sadece bir skalerdir, yani bir anlamı yoktur. Bir yöntem parametre olarak int aldığında, yöntem adının amacı ifade etme işinin tamamını yapması gerekir. Aynı yöntem parametre olarak bir Saat alıyorsa, neler olduğunu görmek çok daha kolay.

Aynı belge ek bir yararın olduğunu açıklıyor:

Hour ya da Money gibi küçük nesneler de, diğer sınıfların etrafında tersine çevrilecek olan davranışları koymak için bize açık bir yer sağlar .

Gerçekten, ilkel maddeler kullanıldığında, bu türlerle ilgili kodun tam konumunu izlemek, genellikle ciddi kod çoğaltmasına yol açan son derece zordur . Eğer Price: Moneysınıf varsa, aradaki menzili kontrol etmek doğaldır. Bunun yerine, ürün fiyatlarını depolamak için int(daha kötüsü a double) kullanılıyorsa, aralığı kim doğrulamalıdır? Ürün? Para iadesi? Sepet?

Son olarak, belgede bahsedilmeyen üçüncü bir fayda, dayanak tipini nispeten kolay değiştirme yeteneğidir. Bugün benim ise ProductIdsahip shortaltta yatan türü olarak ve sonra ben kullanmak gerekir intbunun yerine, büyük ihtimalle tüm kod tabanını yayılan olmaz değiştirmek için kod bulunmaktadır.

Dezavantajı - ve aynı argüman Nesne Calisthenics egzersizinin her kuralı için de geçerlidir - hızlı bir şekilde her şey için bir sınıf oluşturmak için çok zorlayıcı hale gelir . Eğer Productiçeriyorsa ProductPricedevralır hangi PositivePricedevraldığı hangi Pricesırayla devraldığı hangi Money, bu değil temiz mimarisi, daha ziyade tek bir şey bulmak için, bir sürdürme birkaç düzine her zaman dosyaları açılmalıdır tam bir felaket.

Dikkate alınması gereken bir başka nokta, ek sınıflar yaratmanın maliyetidir (kod satırları açısından). Sargılar değişmez ise (genellikle olması gerektiği gibi), C # alırsak sargı içinde en azından:

  • Mülkiyet alıcı,
  • Destek alanı
  • Değeri destek alanına atayan bir yapıcı,
  • Özel bir ToString(),
  • XML dokümantasyonu yorumlar ( çok fazla satır yapar ),
  • A Equalsve GetHashCodegeçersiz kılmalar (ayrıca çok fazla LOC).

ve sonunda ilgili olduğunda:

  • Bir DebuggerDisplay özelliği,
  • Bir geçersiz kılma ==ve !=işleçler,
  • Sonunda örtülü dönüştürme işlecinin, kapsüllenmiş türe sorunsuz bir şekilde dönüştürmek için aşırı yüklenmesi,
  • Kod sözleşmeleri (üç niteliği ile oldukça uzun bir yöntem olan değişmez dahil),
  • XML serileştirme, JSON serileştirme sırasında veya veritabanına bir değer kaydetme / yükleme sırasında kullanılacak birkaç dönüştürücü.

Basit bir sarıcı için yüz LOC onu engelleyici kılar, bu yüzden bu sargının uzun vadeli kârlılığından tamamen emin olabilirsiniz. Thomas Junk tarafından açıklanan kapsam kavramı burada özellikle önemlidir. ProductIdKullanılan tüm kod tabanınızı temsil etmek için yüzlerce LOC yazmak oldukça yararlı görünüyor. Tek bir yöntemde üç satır yapan bir kod parçası için bu boyutta bir sınıf yazmak çok daha şüphelidir.

Sonuç:

  • (1) hataları azaltmaya yardımcı olduğunda (2) kod çoğaltma riskini azaltırken veya (3) daha sonra temel türünü değiştirmeye yardımcı olurken, uygulamanın işletme alanında anlamı olan sınıflara ilkelleri sarın.

  • Kodunuzda bulduğunuz her ilkeli otomatik olarak silmeyin: kullanmanın stringveya inttamamen iyi kullanmanın bir çok durumu vardır .

Uygulamada, içinde sınıf yerine public string CreateNewThing()bir örnek döndürmek yardımcı olabilir, ancak şunları da yapabilirsiniz:ThingIdstring

  • Id<string>Alttaki türün bir dize olduğunu belirten genel bir tür nesnesi olan bir sınıf örneği döndür. Birçok türde bakım yapmak zorunda olmamanın dezavantajı olmadan okunabilirlik avantajına sahipsiniz.

  • Bir Thingsınıf örneği döndür. Kullanıcı sadece kimliğe ihtiyaç duyarsa, bu kolayca yapılabilir:

    var thing = this.CreateNewThing();
    var id = thing.Id;
    

33
Burada altta yatan tipi serbestçe değiştirme gücünün önemli olduğunu düşünüyorum. Bugün bir dize, ama belki bir GUID veya uzun bir yarın. Tip sarmalayıcı oluşturmak, uzaktaki bilgilerin daha az değişmesine neden olan bu bilgileri gizler. ++ Burada iyi cevap.
RubberDuck

4
Para durumunda, para biriminin Money nesnesine dahil edildiğinden veya tüm programınızın aynı olanı kullandığından emin olun (o zaman belgelenmelidir).
Paŭlo Ebermann

5
@Blrfl: derin mirasın gerekli olduğu geçerli durumlar varken, genellikle aşırı LOC tarafından tasarlanan kodda okunabilirliğe dikkat etmeden, zorunlu LOC tarafından ödeme tutumundan kaynaklanan kalıtımı görüyorum. Böylece cevabımın bu kısmının negatif karakteri.
Arseni Mourzenko

3
@ArTs: "Bazı insanlar yanlış şekilde kullandığı için aracı mahkum edelim" argümanı.
Blrfl

6
@MainMa Anlamın pahalı bir hatadan kaçınabileceği yerlere bir örnek: en.wikipedia.org/wiki/Mars_Climate_Orbiter#Cause_of_failure
cbojar

60

Kapsamı bir kural olarak kullanırdım : Böyle üretme ve tüketme kapsamı ne kadar dar olursa values, bu değeri temsil eden bir nesne yaratma olasılığınız o kadar düşüktür.

Aşağıdaki sözde kodun olduğunu söyleyin

id = generateProcessId();
doFancyOtherstuff();
job.do(id);

o zaman kapsam çok sınırlıdır ve onu bir tür haline getirmenin hiçbir anlamı yoktur. Ancak, bu değeri bir katmanda yaratıp başka bir katmana (veya başka bir nesneye) aktardığınızı söyleyin, o zaman bunun için bir tür oluşturmak mükemmel olur.


14
Çok iyi bir nokta. Açık türler , özellikle nesne ve hizmet sınırları arasında önemli olan, niyetini iletmek için önemli bir araçtır .
Avner Shahar-Kashtan

+1 tüm kodlarını doFancyOtherstuff()bir alt yordamda henüz yeniden etkinleştirmediyseniz, başvurunun job.do(id)basit bir dize olarak tutulacak kadar yerel olmadığını düşünebilirsiniz .
Mark Hurd

Bu tamamen doğru! Dış dünyanın asla bu yöntemi kullanmayacağını bildiğiniz özel bir yönteminiz olduğunda geçerlidir. Ondan sonra, sınıf korunurken ve bundan sonra sınıf halka açıkken, halka açık bir apinin parçası olmadığında, biraz daha fazla düşünebilirsiniz. Kapsam kapsamı ve çizgiyi çok ilkel ve çok özel türler arasında iyi bir konuma yerleştirmek için birçok sanat.
Samuel,

34

Statik tip sistemler, verilerin yanlış kullanımının önlenmesi ile ilgilidir.

Bunu yapan bariz tür örnekleri var:

  • bir UUID ayını alamazsın
  • İki dizgiyi çarpamazsınız.

Daha ince örnekler var

  • masanın uzunluğunu kullanarak bir şey için ödeme yapamazsınız
  • Birinin adını URL olarak kullanarak bir HTTP isteği yapamazsınız.

doubleHem fiyat hem de uzunluk için kullanmak isteyebilir veya stringhem isim hem de URL için a kullanabilirsiniz . Ancak bunu yapmak, müthiş tip sistemimizi altüst eder ve bu yanlış kullanımların dilin statik kontrollerini geçmesini sağlar.

Pound-saniye Newton-saniye ile karıştırılmak çalışma zamanında kötü sonuçlar doğurabilir .


Bu, özellikle dizelerde bir sorundur. Sık sık "evrensel veri türü" olurlar.

Öncelikle bilgisayarlarla arayüzleri metinlendirmeye alışkınız ve sıklıkla bu insan arayüzlerini (UI) programlama arayüzlerine (API) genişletiyoruz . Biz karakter olarak 34.25 düşünmek 34.25 . 05-03-2015 karakterleri olarak bir tarih düşünürüz . Bir UUID'yi 75e945ee-f1e9-11e4-b9b2-1697f925ec7b karakterleri olarak düşünüyoruz .

Ancak bu zihinsel model API soyutlamasına zarar vermektedir.

Sözler veya dil, yazıldığı veya söylendiği gibi, düşünce mekanizmamda hiçbir rol oynamıyor gibi görünüyor.

Albert Einstein

Benzer şekilde, metinsel gösterimler tür ve API tasarımında hiçbir rol oynamamalıdır. Dikkat string! (ve diğer aşırı genel "ilkel" türler)


Tipler “işlemlerin ne anlama geldiğini” iletir.

Örneğin, bir keresinde bir istemcide bir HTTP REST API'si için çalıştım. REST, uygun şekilde yapıldığında, ilgili varlıklara işaret eden köprülere sahip olan hiper medya varlıkları kullanır. Bu müşteride, yalnızca yazılan varlıklar değil (örneğin, Kullanıcı, Hesap, Abonelik), bu varlıklara bağlantılar da yazılmıştır (UserLink, AccountLink, SubscriptionLink). Bağlantılar etrafındaki sarmalayıcılardan biraz daha fazlaydı Uri, ancak ayrı türler bir Kullanıcı almak için bir AccountLink kullanmayı denemeyi imkansız hale getirdi. Her şey çok açık olsaydı Uri, hatta daha da kötüsü stringolsaydı , bu hatalar yalnızca çalışma zamanında bulunabilirdi.

Aynı şekilde, sizin durumunuzda, yalnızca bir amaç için kullanılan verilere sahipsiniz: tanımlamak için Operation. Başka bir şey için kullanılmamalı ve Operationoluşturduğumuz rasgele dizelerle s tanımlamaya çalışmamalıyız. Ayrı bir sınıf oluşturmak, kodunuza okunabilirlik ve güvenlik sağlar.


Tabii ki, tüm iyi şeyler aşırı kullanılabilir. Düşünmek

  1. kodunuza ne kadar netlik kattığını

  2. ne sıklıkla kullanıldığı

Bir "veri tipi" (soyut anlamda) sık sık, farklı amaçlar için ve kod arayüzleri arasında kullanılıyorsa, ayrıntıya düşme pahasına, ayrı bir sınıf olmak için çok iyi bir adaydır.


21
"dizeler genellikle" evrensel "veri türü" olur - bu, "dilde yazılan dillerin" dize dilinin kökenidir.
MSalters

@ MSalters, ha ha, çok güzel.
Paul Draper,

4
Ve elbette, bir tarih olarak yorumlandığında ne anlama 05-03-2015 geliyor ?
bir CVn

10

Güvenlik nedenleriyle basit türleri bir sınıfa sarmanın iyi bir uygulama olduğunu düşünüyor musunuz?

Ara sıra.

Bu, stringdaha somut bir şey kullanmaktan doğabilecek sorunları tartmanız gereken durumlardan biridir OperationIdentifier. Şiddeti nedir? Onların olasılığı nedir?

O zaman diğer tür kullanmanın maliyetini göz önünde bulundurmanız gerekir. Kullanması ne kadar acı verici? Ne kadar iş yapmak gerekiyor?

Bazı durumlarda, üzerinde çalışabileceğiniz güzel bir somut türü bulunduğunda zamandan ve emekten tasarruf edersiniz. Diğerlerinde, sorun olmaya değmez.

Genel olarak, bu tür bir şey bugün olduğundan daha yapılması gerektiğini düşünüyorum. Etki alanınızda bir şey ifade eden bir varlığa sahipseniz, bu varlığın işletmeyle birlikte değişmesi / büyümesi daha muhtemel olduğundan, kendi türünde olması iyidir.


10

Genelde, ilkeler ve dizeler için bir tür oluşturmanız gerektiğine katılıyorum, ancak yukarıdaki cevaplar çoğu durumda bir tür oluşturmayı önerdiğinden, neden / ne zaman olmamasının birkaç nedenini listeleyeceğim:

  • Verim. Buradaki gerçek dillere bakmalıyım. C # 'da, eğer bir müşteri kimliği kısa ise ve bunu bir sınıfa sararsanız - az önce çok fazla yük yarattınız: hafıza, çünkü şimdi 64 bitlik bir sistemde 8 bayttır ve şimdiye kadar yığında tahsis edilmiştir.
  • Tür üzerinde varsayımlarda bulunduğunuzda. Bir müşteri kimliği kısaysa ve bir şekilde paketleyen bir mantığa sahipseniz - genellikle tür üzerinde varsayımlar yaparsınız. Tüm bu yerlerde, şimdi soyutlamayı kırmanız gerekiyor. Tek nokta, önemli bir şey değil, her yere yayılmışsa, ilkel kullandığınız zamanın yarısını bulabilirsiniz.
  • Çünkü her dilde typedef yoktur. Ve zaten yazılmamış ve kodlanmış diller için, böyle bir değişiklik yapmak, hataları bile ortaya çıkarabilecek büyük bir görev olabilir (ama herkes iyi kapsamaya sahip bir test odasına sahip değil mi?).
  • Bazı durumlarda okunabilirliği azaltır . Bunu nasıl basabilirim? Bunu nasıl doğrularım? Boş mu kontrol etmeliyim? Gereksinim duyacağınız tüm sorular, türün tanımına odaklanmanızı gerektirir.

3
Şüphesiz, sarıcı türü bir sınıftan ziyade bir yapıysa, o zaman short(örneğin) sarılmış veya açılmamış aynı maliyete sahiptir. (?)
Matematiksel

1
Bir türün kendi geçerliliğini kapsaması iyi bir tasarım olmaz mıydı? Veya doğrulamak için bir yardımcı tipi oluşturmak için?
Nick Udell

1
Gereksiz yere paketleme veya munging, anti-paterndir. Bilginin, dönüşümün açıkça ve gerçekten gerekli olduğu durumlar dışında, uygulama kodu üzerinden şeffaf bir şekilde akması gerekir . Tipler iyidir, çünkü sistemin her yerine sıçramış büyük miktarda kötü uygulama için, tek bir yerde bulunan az miktarda iyi kodu yerine koyarlar.
Thomas W

7

Hayır, "her şey" için türler (sınıflar) tanımlamamalısınız.

Ancak, diğer cevapların belirttiği gibi, bunu yapmak için genellikle yararlıdır. Kodunuzu yazarken, test ederken ve sürdürürken uygun bir tür veya sınıfın bulunmamasından dolayı - bilinçli olarak mümkünse - çok fazla sürtünme hissi veya duygusu geliştirmelisiniz . Benim için, çok fazla sürtünme başlangıcı, çoklu ilkel değerleri tek bir değer olarak birleştirmek istediğimde veya değerleri doğrulamak istediğimde (yani, ilkel türün tüm olası değerlerinden hangisinin geçerli değerlere karşılık geldiğini belirlemek) 'zımni tür').

Kodumda çok fazla tasarımdan sorumlu olmak için sorunuzda neyi dile getirdiğiniz gibi düşünceler buldum . Kasıtlı olarak gereğinden fazla kod yazmaktan kaçınma alışkanlığı geliştirdim. Umarım kodunuz için iyi (otomatik) testler yazıyorsunuzdur - eğer öyleyseniz, kodunuzu kolayca yeniden değerlendirebilir ve bunu yaparken türler veya sınıflar ekleyebilirsiniz, böylece kodunuzun devam eden gelişimi ve bakımı için net faydalar sağlar .

Telastyn'in cevabı ve Thomas Junk'in cevabı hem ilgili kodun bağlamı hem de kullanımı hakkında çok iyi bir noktaya değindi . Tek bir kod bloğu içinde bir değer kullanıyorsanız (örneğin, yöntem, döngü, usingblok), o zaman ilkel bir tür kullanmak iyidir. Tekrar tekrar ve başka birçok yerde bir dizi değer kullanıyorsanız, ilkel bir tür kullanmak bile iyidir. Fakat bir dizi değeri ne kadar sıklıkla ve yaygın olarak kullanıyorsanız ve bu değerler kümesi ne kadar az olursa olsun, ilkel tip tarafından temsil edilen değerlere ne kadar yakınsa, o kadar çok bir sınıf veya tipte değerleri içine almayı düşünmelisiniz.


5

Yüzeyde yapmanız gereken tek şey bir işlem tanımlamak gibi görünüyor.

Ek olarak bir işlemin ne yapması gerektiğini söylersiniz:

için operasyon başlatmak için [ve] onun bitirmek izlemek

Bunu, “tam olarak nasıl kullanıldığını” gösteriyormuş gibi söylüyorsunuz, ancak bunların bir işlemi tanımlayan özellikler olduğunu söyleyebilirim. Bana bir tür tanım gibi geliyor , "komut kalıbı" adı verilen ve hatta çok iyi uyan bir kalıp bile var. .

Diyor ki

komut düzeni [...], bir eylemi gerçekleştirmek veya daha sonra bir olayı tetiklemek için gereken tüm bilgileri kapsüllemek için kullanılır .

Bunun operasyonlarınızda yapmak istediklerinize kıyasla çok benzer olduğunu düşünüyorum. (Her iki alıntıda da kalın harflerle yazdığım sözcükleri karşılaştırın.) Bir dize döndürmek yerine Operation, soyut anlam için bir tanımlayıcı , örneğin oop'taki o sınıfın bir nesnesine bir işaretçi döndürün.

Yorum ile ilgili olarak

Bu sınıfa bir komut diyerek wtf-y olur ve sonra onun içinde bir mantık olmaz.

Hayır olmazdı. Unutmayın desenler çok soyut , bunlar biraz meta olmasına kadar soyut. Başka bir deyişle, bazı gerçek dünya kavramlarını değil, programlamanın kendisini soyutlarlar. Komut kalıbı fonksiyon çağrısının bir soyutlamasıdır. (veya yöntem) Sanki birisi parametrelerin değerlerini geçtikten hemen sonra ve yürütmeden hemen önce duraklat düğmesine basarsa, daha sonra devam ettirilir.

Aşağıdakiler oop'u düşünüyor, ancak arkasındaki motivasyon herhangi bir paradigma için geçerli olmalı. Mantığı komuta yerleştirmenin neden kötü bir şey olarak kabul edilebileceği konusunda bazı nedenler vermeyi seviyorum.

  1. Komutadaki bütün o mantığı senin elinde tuttuysan, şişirilmiş ve dağınık olurdu. "Ah, bütün bu işlevsellik ayrı sınıflarda olmalı" diye düşünecektiniz. Sen olur planı ayrı komuta dışına mantığı.
  2. Komutta bu kadar mantık varsa, test ederken test etmek ve bir araya gelmek zor olurdu. "Kutsal bok, neden bu komutu saçmalık kullanmak zorundayım? Bu işlev 1'i dağıtırsa ya da olmasa da test etmek istiyorum, daha sonra aramak istemiyorum, şimdi test etmek istiyorum!"
  3. Komutta o kadar mantıklı olsaydın, bittiğinde rapor vermen çok mantıklı olmazdı. Varsayılan olarak eşzamanlı olarak yürütülecek bir işlev düşünürseniz, yürütmeyi bitirdiğinde bildirimde bulunmanın bir anlamı yoktur. Belki başka bir iş parçacığı başlatıyorsunuzdur, çünkü işleminiz aslında avatarı sinema formatına dönüştürmek (ahududu pi'de ek iş parçacığı gerektirebilir), belki de bir sunucudan bazı verileri almak zorundasınız ... bittiğinde geri rapor, bunun nedeni, bazı eşzamansızlık (bu bir kelime mi?) oluyor olabilir. Bir iş parçacığı çalıştırmak veya bir sunucuyla bağlantıya geçmek, sizin emrinizde olması gerekmeyen bir mantık. Bu biraz meta tartışması ve belki de tartışmalı. Mantıklı gelmediğini düşünüyorsanız, lütfen yorumlarda bana söyleyin.

Özetlemek için: komut deseni, daha sonra çalıştırılmak üzere işlevselliği bir nesneye sarmanıza izin verir. Modülerlik adına (işlevsellik, komutla çalıştırılıp çalıştırılmadığına bakılmaksızın var olur), test edilebilirlik (fonksiyonellik komut olmadan test edilebilir olmalıdır) ve temel olarak iyi kod yazma talebini ifade eden diğer tüm vızıltı sözcükleri için, gerçek mantığı koymazsınız komuta


Desenler soyut olduğu için, iyi gerçek dünya metaforlarını bulmak zor olabilir. İşte bir girişim:

"Hey büyükanne, lütfen 12'deki TV'deki kayıt düğmesine basar mısın böylece kanal 1'deki simpsonları kaçırmamalıyım?"

Büyükannem teknik olarak o kayıt düğmesine bastığında ne olacağını bilmiyor. Mantık başka bir yerde (TV'de). Ve bu iyi bir şey. İşlevsellik enkapsüle edilir ve bilgiler komuttan gizlenir, bu bir mantığın bir parçası değil, bir API kullanıcısıdır ... aman Tanrım, yine vızıltı sözler gibiyim, bu düzenlemeyi şimdi bitirmeliyim.


Haklısın, böyle düşünmedim. Ancak komut deseni% 100 uygun değil çünkü işlemin mantığı başka bir sınıfa yerleştirilmiş ve onu Komut olan nesnenin içine koyamıyorum. Bu sınıfa bir komut diyerek wtf-y olur ve sonra onun içinde bir mantık olmaz.
Ziv

Bu çok mantıklı. Bir OperationIdentifier’ı değil, bir İşlemi döndürmeyi düşünün. Bu, bir Görev veya Görev <T> döndürdüğünüz C # TPL'ye benzer. @ Ziv: "İşlemin mantığı başka bir sınıfta kapsüllenmiş" diyorsunuz. Fakat bu gerçekten, buradan da sağlayamayacağınız anlamına mı geliyor?
Moby Disk

@ Ziv Farklı olmak için yalvarıyorum. Cevaplarıma eklediğim nedenlere bir göz atın ve size bir anlam ifade edip etmediklerini görün. =)
null

5

İlkel tiplerin sarılmasının ardındaki fikir,

  • Alana özel dil kurmak
  • Yanlış değerler ileterek kullanıcılar tarafından yapılan hataları sınırlayın

Açıkçası, her yere yapmak çok zor ve pratik olmazdı, ama gereken yerlere sarmak önemlidir.

Örneğin, Order sınıfınız varsa,

Order
{
   long OrderId { get; set; }
   string InvoiceNumber { get; set; }
   string Description { get; set; }
   double Amount { get; set; }
   string CurrencyCode { get; set; }
}

Bir Sipariş aramak için önemli özellikleri çoğunlukla OrderId ve InvoiceNumber'dır. Ve Miktar ve Para Birimi yaklaşımı yakından ilişkilidir, burada eğer Birisi Sipariş Miktarını değiştirmeden Para Birimi'ni değiştirirse, artık geçerli olarak kabul edilemez.

Bu nedenle, yalnızca OrderId, InvoiceNumber'ı sarmalamak ve para birimi için bir bileşik tanıtımı bu senaryoda anlamlıdır ve muhtemelen ambalaj açıklaması bir anlam ifade etmemektedir. Bu yüzden tercih edilen sonuç,

    Order
    {
       OrderIdType OrderId { get; set; }
       InvoiceNumberType InvoiceNumber { get; set; }
       string Description { get; set; }
       Currency Amount { get; set; }
    }

Yani her şeyi sarmak için bir neden yok, ama gerçekten önemli olan şeyler.


4

Popüler olmayan görüş:

Genellikle yeni bir tür tanımlamamalısınız!

İlkel veya temel bir sınıfı sarmak için yeni bir tür tanımlamak, bazen sahte bir tür tanımlamak olarak adlandırılır. IBM bunu burada kötü bir uygulama olarak nitelendiriyor (özellikle bu durumda jeneriklerle yanlış kullanıma odaklanıyorlar).

Sözde türler ortak kütüphane işlevselliğini işe yaramaz hale getirir.

Java'nın Matematik işlevleri, tüm sayı ilkelleriyle çalışabilir. Fakat eğer yeni bir sınıf tanımlarsanız Yüzde (0 ~ 1 aralığında olabilen bir çifte sarma) tüm bu işlevler işe yaramaz ve Yüzde sınıfının içsel temsili hakkında bilmesi gereken (hatta daha da kötüsü) sınıflarla sarılması gerekir. .

Sözde türleri viral gitmek

Birden fazla kütüphane yaparken, genellikle bu sahte ilaçların viral hale geldiğini göreceksiniz. Yukarıda belirtilen, bir kitaplıktaki Yüzde sınıfını kullanırsanız, onu kütüphane sınırına dönüştürmeniz gerekir (tüm güvenlik / anlam / mantık / bu tür için kullandığınız diğer nedenleri kaybederek) veya bu sınıfları yapmanız gerekir. diğer kütüphanelere de erişilebilir. Yeni kütüphaneye türlerinizle basit bir çifte yetişmiş olabilecek şekilde bulaştırmak.

Mesajı al

Sardığınız türün çok fazla iş mantığına ihtiyacı olmadığı sürece, sözde bir sınıfa sarılmasını tavsiye ederim. Bir sınıfı yalnızca ciddi iş kısıtlamaları varsa sarmalısınız. Diğer durumlarda değişkenlerinizi doğru adlandırmak, anlam ifade etmede uzun bir yol kat etmelidir.

Bir örnek:

A uint, bir UserId öğesini mükemmel şekilde temsil edebilir, Java'nın yerleşik operatörlerini uints için (== gibi) kullanmaya devam edebiliriz ve kullanıcı kimliğinin 'iç durumunu' korumak için iş mantığına ihtiyacımız yoktur.


Anlaşmak. tüm programlama felsefesi gayet iyi, ama pratikte de çalışmasını sağlamak zorundasın
Ewan

İngiltere'deki en fakir insanlara borç verdiğiniz herhangi bir ilan
gördüyseniz, 0,1

1
C / C ++ / Objective C'de, varolan bir ilkel tür için yalnızca yeni bir ad veren bir typedef kullanabilirsiniz. Öte yandan, kodunuz ne tür olursa olsun çalışmalıdır, örneğin OrderId'i uint32_t'den uint64_t'a değiştirirsem (çünkü 4 milyardan fazla sipariş işlemem gerekir).
gnasher729

Cesaretin için seni küçümsemeyeceğim, ancak sözde türler ve toplanan cevapta tanımlanan türler farklı. Bunlar işletmeye özgü türlerdir. Psuedotypes hala genel olan türlerdir.
0

@ gnasher729: C ++ 11, sarma işlemini kullanıcı için çok tatlı kılan kullanıcı tanımlı hazır bilgi kaynaklarına sahiptir: Mesafe d = 36.0_mi + 42.0_km; . msdn.microsoft.com/en-us/library/dn919277.aspx
damix911

3

Tüm ipuçlarında olduğu gibi, kuralları ne zaman uygulayacağınızı bilmek beceridir. Kendi türlerinizi yazı tipi bir dilde yaratırsanız, yazım denetimi yapılır. Genelde bu iyi bir plan olacak.

NamingConvention okunabilirliğin anahtarıdır. İki kombine niyeti açıkça iletebilir.
Ancak /// hala faydalıdır.

Öyleyse evet, yaşamları sınıf sınırlarının ötesindeyken kendi türlerimi yaratacağımı söyleyebilirim. Ayrıca her zaman Structve Classher zaman Sınıf’ın kullanılmamasını düşünün .


2
Ne ///demek istiyorsun?
Gabe

1
/// C # 'daki dökümantasyon yorumu olup, intellisense'in imzanın dökümanlarını çağrı metot noktasında göstermesini sağlar. Kodu belgelemenin en iyi yolunu düşünün. Araçlar tarafından yetiştirilebilir ve zekâ davranışına ulaşma imkanı sunar. msdn.microsoft.com/en-us/library/tkxs89c5%28v=vs.71%29.aspx
phil soady
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.