Bir at sürüsü göz önüne alındığında, tek boynuzlu atların ortalama boynuz uzunluğunu nasıl bulabilirim?


30

Yukarıdaki soru, eski kodda karşılaştığım ortak bir problemin veya bu problemi çözmek için önceki girişimlerden kaynaklanan problemlerin daha doğru bir örneğidir.

Yöntem gibi bu sorunu çözmeyi amaçlayan en az bir .NET framework yöntemi düşünebilirim Enumerable.OfType<T>. Ama nihayetinde çalışma zamanında bir nesnenin türünü sorguya çekmenizin gerçeği benimle tam oturmuyor.

Her ata bir tek boynuzlu at mısın? Aşağıdaki yaklaşımlar da akla geliyor:

  • Tek boynuzlu at boynuzunun uzunluğunu almak için bir girişimde bulunulduğunda bir istisna atın (her at için uygun olmayan işlevleri gösterir)
  • Tek boynuzlu atı olmayan bir boynuzun uzunluğu için varsayılan veya sihirli bir değer döndürün (tek boynuzlu atları olmayan bir grup at üzerinde boynuz istatistiklerini kırmak isteyen herhangi bir kod boyunca varsayılan olarak biberli kontroller gerektirir)
  • Mirastan kurtulun ve atın tek boynuzlu at olup olmadığını söyleyen bir at üzerinde ayrı bir nesne oluşturun.

Bunun en iyi "cevapsız" olarak yanıtlanacağını hissediyorum. Ancak bu soruna nasıl yaklaşıyorsunuz ve eğer değişirse, kararınızdaki bağlam nedir?

Ayrıca, bu problemin hala işlevsel kodda var olup olmadığıyla ilgili herhangi bir görüş de ilgimi çeker (belki de sadece değişkenliği destekleyen işlevsel dillerde var mıdır?)

Bu, aşağıdaki sorunun olası bir kopyası olarak işaretlendi: Aşağıya indirme nasıl engellenir?

Bu sorunun cevabı, birinin elinde olduğunu varsayar. HornMeasurer tüm korna ölçümlerinin yapılması gerektiğine . Ancak bu eşitlikçi ilke çerçevesinde oluşturulmuş bir kod temeli üzerine herkesin bir atın boynuzu ölçmekte özgür olması gerektiği yönünde bir açıklama.

Yok bir HornMeasurer, kabul cevabım yaklaşımı aynaları istisna temelli yaklaşım yukarıda listelenen.

Ayrıca atların ve tek boynuzlu atların birbirinin aynı olup olmadığı veya tek boynuzlu atların atların sihirli bir alt türü olup olmadığına dair yorumlarda bazı karışıklıklar var. Her iki olasılık da göz önünde bulundurulmalı - belki bir tanesi diğerine tercih edilebilir mi?


22
Atların boynuzları yoktur, bu nedenle ortalama tanımsızdır (0/0).
Scott Whitlock

3
@moarboilerplate 10'dan sonsuza kadar her yerde.
dadı

4
@StephenP: Bu durum matematiksel olarak işe yaramazdı; tüm bu 0'lar ortalamayı çarpıtır.
Mason Wheeler,

3
Eğer sorunuzun cevabı olmayan bir cevapla en iyi şekilde cevaplanıyorsa, soru-cevap sitesine ait değildir; reddit, quora veya diğer tartışmaya dayalı siteler, yanıt vermeyen türden şeyler için oluşturulmuş ... diyor ki, @MasonWheeler kodunu arıyorsanız, hiçbir fikrim olmadığını sanıyorsam açıkça yanıtlanabileceğini düşünüyorum sormaya çalıştığınız şey ..
Jimmy Hoffa,

3
@JimmyHoffa "yanlış yapıyorsun" kabul edilebilir bir "cevap vermeme" olur ve çoğu zaman "iyi, işte yapabileceğin bir yoldan" daha iyidir - genişletilmiş bir tartışma gerekmez.
moarboilerplate

Yanıtlar:


11

UnicornÖzel bir tür muamelesi yapmak istediğinizi varsayarsak Horse, temelde modellemenin iki yolu vardır. Daha geleneksel yol alt sınıf ilişkisidir. Listeleri her zaman önemli olduğu bağlamlarda ayrı tutmak için kodunuzu yeniden takarak türü ve aşağı yayınları kontrol etmekten kaçınabilirsiniz ve bunları yalnızca hiç umursamadığınız bağlamlarda birleştirinUnicorn özellikleri . Başka bir deyişle, bunu ilke olarak at sürülerinden tek boynuzlu atları çıkarmanız gereken duruma gelmemeniz için düzenlersiniz. Bu başlangıçta zor görünüyor, ancak vakaların% 99,99'unda mümkün ve genellikle kodunuzu çok daha temiz hale getiriyor.

Bir tek boynuzlu atı modellemenin bir başka yolu da, tüm atlara isteğe bağlı bir boynuz uzunluğu vermektir. Daha sonra boynuzlu olup olmadığını kontrol ederek tek boynuzlu at olup olmadığını test edebilir ve tüm tek boynuzlu atların ortalama boynuz uzunluğunu (Scala'da) bulabilirsiniz:

case class Horse(val hornLength: Option[Double])

val horse = Horse(None)
val unicorn = Horse(Some(12.0))
val anotherUnicorn = Horse(Some(6.0))

val herd = List(horse, unicorn, anotherUnicorn)
val hornLengths = herd flatMap {_.hornLength}
val averageLength = hornLengths.sum / hornLengths.size

Bu yöntem, tek bir sınıfla daha basit olma avantajına sahiptir, ancak daha az genişletilebilir olmanın dezavantajı ve "tek boynuzlu atlık" için bir çeşit dolambaçlı yol bulma biçiminin dezavantajı vardır. Bu çözümle devam etmenin püf noktası, daha esnek bir mimariye geçmeniz gerektiğini sık sık genişletmeye başladığınızı fark etmektir. Bu tür bir çözüm, öğeleri flatMapkolayca filtrelemek gibi basit ve güçlü işlevlere sahip olduğunuz fonksiyonel dillerde çok daha popülerdir None.


7
Tabii ki, bu sıradan bir at ve tek boynuzlu at arasındaki tek fark boynuz olduğunu varsayar. Durum böyle değilse, işler çok daha karmaşık hale gelir.
Mason Wheeler

@MasonWheeler sadece sunulan ikinci yöntemde.
moarboilerplate

1
Tek boynuzlu atların ve tek boynuzlu atların, tek boynuzlu atları umursamadığınız bir bağlamda olmadıkça, bir miras senaryosunda asla birlikte kalemelarına ilişkin yorumlar üzerine gelin. Tabi, .OfType () sorunu çözebilir ve işleri halledebilir, ancak ilk başta bile olmaması gereken bir problemi çözüyor. İkinci yaklaşıma gelince, işe yarıyor çünkü seçenekler bir şeyi ima etmek için boşuna dayanmaktan çok daha üstün. Tek boynuzlu at özelliklerini tek başına bir özelliğe yerleştirirseniz ve aşırı derecede uyanık olursanız, ikinci yaklaşımın OO'da bir uzlaşma ile sağlanabileceğini düşünüyorum.
moarboilerplate

1
Tek boynuzlu at özelliklerini tek başına bir mülkün içine yerleştiriyorsanız ve aşırı derecede uyanıksanız , uzlaşma - neden kendiniz için hayatı zorlaştırıyorsunuz? Doğrudan typeof kullanın ve gelecek bir tondan tasarruf edin.
gbjbaanb

@gbjbaanb Bu yaklaşımın yalnızca bir aneminin Horsebir IsUnicornözelliği ve UnicornStuffüzerinde boynuz uzunluğu olan bir tür özelliğe sahip olduğu senaryolar için gerçekten uygun olduğunu düşünürdüm (sorunuzda belirtilen binici / parıltı ölçeklenirken).
moarboilerplate

38

Tüm seçenekleri hemen hemen ele aldınız. Belirli bir alt türe bağlı davranışınız varsa ve diğer türlerle karıştırılıyorsa, kodunuzun bu alt türden haberdar olması gerekir; bu basit bir mantıksal akıl yürütmedir.

Şahsen ben sadece giderdim horses.OfType<Unicorn>().Average(u => u.HornLength). Kodun amacını çok açık bir şekilde ifade eder; bu, birinin daha sonra sürdürmek zorunda kalmasından bu yana en önemli şeydir.


Lambda sözdizimim doğru değilse lütfen beni affet; Ben bir C # kodlayıcıdan pek değilim ve bu gibi gizli detayları asla saklayamam. Yine de ne demek istediğim açık olmalı.
Mason Wheeler

1
Endişeye gerek yok, liste Unicornzaten yine de s içerdiğinde sorun çözülmez (ihmal edebileceğiniz kayıt için return).
moarboilerplate

4
Sorunu hızlı bir şekilde çözmek isteseydim bu, giderdim cevap budur. Ama cevabı değil, kodun daha makul olması için yeniden denemek istersem.
Andy

6
Saçma bir optimizasyon seviyesine ihtiyaç duymadığınız sürece bu kesinlikle bir cevap. Açıklığı ve okunabilirliği, hemen hemen her şeyin tartışılmasını sağlar.
David, 22: 51'de Monica

1
@DavidGrinberg ya bu temiz, okunabilir yöntemi yazarsanız, önce var olmayan bir miras yapısını uygulamanız gerektiği anlamına geliyordu?
moarboilerplate

9

.NET ile yanlış bir şey yok:

var unicorn = animal as Unicorn;
if(unicorn != null)
{
    sum += unicorn.HornLength;
    count++;
}

Linq eşdeğerini kullanmak da iyidir:

var averageUnicornHornLength = animals
    .OfType<Unicorn>()
    .Select(x => x.HornLength)
    .Average();

Başlıkta sorduğunuz soruya göre, bulmayı beklediğim kod budur. Eğer soru “boynuzlu hayvanların ortalaması nedir” gibi bir şey sorsa, bu farklı olurdu:

var averageHornedAnimalHornLength = animals
    .OfType<IHornedAnimal>()
    .Select(x => x.HornLength)
    .Average();

Linq kullanılırken, Average(ve Minve Max), numaralandırılabilir boş ve T tipinin geçersiz olmadığı durumlarda bir istisna atacağını unutmayın. Bunun nedeni ortalamanın gerçekten tanımsız (0/0) olmasıdır. Yani gerçekten böyle bir şeye ihtiyacınız var:

var hornedAnimals = animals
    .OfType<IHornedAnimal>()
    .ToList();
if(hornedAnimals.Count > 0)
{
    var averageHornLengthOfHornedAnimals = hornedAnimals
        .Average(x => x.HornLength);
}
else
{
    // deal with it in your own way...
}

Düzenle

Bunun eklenmesi gerektiğini düşünüyorum ... bunun gibi bir sorunun nesne yönelimli programcılarla iyi oturmamasının sebeplerinden biri, bir veri yapısını modellemek için sınıfları ve nesneleri kullandığımızı varsaymasıdır. Orijinal Smalltalk-esque nesne yönelimli fikri, programınızı nesne olarak başlatılan ve onlara bir mesaj gönderdiğinizde sizin için hizmet veren modüller dışında yapılandırmaktı. Bir veri yapısını modellemek için sınıfları ve nesneleri de kullanabilmemiz gerçeği (faydalı) bir yan etkidir, ancak bunlar iki farklı şeydir. Eğer beri ben bile, ikincisi nesne yönelimli programlama düşünülmelidir sanmıyorum olabilir a ile aynı şeyi yapmakstruct , ama sadece güzel olarak olmaz.

Sizin için işleri yapan hizmetleri oluşturmak için nesne yönelimli programlama kullanıyorsanız, o zaman bu hizmetin aslında başka bir hizmet mi yoksa somut bir uygulama mı olduğunu sorgulamak, iyi nedenlerle kaşlarını çattı. Size bir arayüz (genellikle bağımlılık enjeksiyonu yoluyla) verilmiştir ve bu arayüzü / sözleşmeyi kodlamanız gerekir.

Öte yandan, eğer bir veri yapısı veya veri modeli oluşturmak için sınıf / nesne / arayüz fikirlerini kullanıyorsanız (yanlış) kullanıyorsanız, şahsen is-a fikrini sonuna kadar kullanma konusunda bir sorun görmüyorum. Tek boynuzlu atların alt tür bir at olduğunu tanımladıysanız ve alanınız içinde tamamen mantıklı geliyorsa, o zaman kesinlikle devam edin ve tek boynuzlu atları bulmak için sürünüzdeki atları sorgulayın. Sonuçta, böyle bir durumda, genellikle çözmemiz gereken sorunların çözümlerini daha iyi ifade etmek için alana özgü bir dil oluşturmaya çalışıyoruz. Bu anlamda yanlış bir şey yok.OfType<Unicorn>() vb. .

Sonuçta, bir öğe toplanması ve türünü filtrelemek, nesne yönelimli programlama değil, sadece işlevsel bir programlamadır. Neyse ki C # gibi diller artık her iki paradigmayı da rahatça kullanabiliyor.


7
Zaten biliyoruz animal bir olduğunu Unicorn ; sadece kullanmak yerine kullanmanız asveya potansiyel olarak daha iyi bir kullanım as için kullanmanız yeterli .
Philip Kendall

3

Ancak, nihayetinde çalışma zamanında bir nesnenin türünü sorguya çekmenizin gerçeği bana uymuyor.

Bu ifadeyle ilgili sorun, hangi mekanizmayı kullanırsanız kullanın, nesneyi ne tür olduğunu söylemek için her zaman sorgulayacağınızdır. Bu RTTI olabilir ya da sorduğunuz bir birlik veya sade veri yapısı olabilirif horn > 0 . Kesin özellikler biraz değişmekle birlikte, niyet aynıdır - nesneyi daha fazla sorgulamanız gerekip gerekmediğini görmek için bir şekilde kendisine soruyorsunuz.

Bu göz önüne alındığında, bunu yapmak için dilinizin desteğini kullanmak mantıklıdır. .NET'te kullanırdıntypeof örneğin .

Bunu yapmanın nedeni sadece dilinizi iyi kullanmanın ötesine geçiyor. Başka bir şeye benzeyen ancak küçük bir değişiklik için bir nesneye sahipseniz, zamanla daha fazla farklılık bulacağınız ihtimali vardır. Tek boynuzlu at / at örneğinizde sadece boynuz uzunluğu olduğunu söyleyebilirsiniz ... ama yarın potansiyel bir binicinin bakire olup olmadığını veya kakın ışıltılı olup olmadığını kontrol etmeye başlayacaksınız. (klasik bir gerçek dünya örneği, ortak bir tabandan türetilen GUI widget'ları olacaktır ve farklı onay kutularını ve liste kutularını aramanız gerekir. Farklılıkların sayısı, verilerin tüm olası izinlerini içeren tek bir süper nesne oluşturmak için çok büyük olurdu. ).

Çalışma zamanında bir nesnenin türünü kontrol etmek iyi sonuç vermiyorsa, alternatifiniz farklı nesneleri en baştan ayırmaktır - tek bir tek boynuzlu at / at sürüsünü saklamak yerine, 2 koleksiyon tutarsınız - biri atlar, biri tek boynuzlu atlar için . Özel bir kapta saklasanız bile bu çok işe yarayabilir (örneğin, anahtarın nesne türü olduğu bir çoklu harita ... ancak daha sonra onları 2 grupta saklasak bile, tekrar nesne türünü sorgulamaktayız) !)

Kesinlikle istisna temelli bir yaklaşım yanlıştır. İstisnaları normal program akışı olarak kullanmak kod kokusudur (tek boynuzlu atlar sürüsü ve kafasına bantlanmış bir deniz kabuğu olan bir eşek sıkışmışsa, istisnaya dayalı yaklaşımın tamam olduğunu söyleyebilirim, ancak tek boynuzlu at sürüsü varsa ve atların her birini tek boynuzlu at için kontrol etmek beklenmedik değildir İstisnalar istisnai durumlar içindir, karmaşık değildir ve en azından koleksiyonunuzu daha hızlı, net bir şekilde işlemek, daha az kod satırı kullanarak ve diğer istisnalar atıldığında ortaya çıkan sorunlardan kaçınmak (ör. boş bir koleksiyon veya eşeğin deniz kabuğunu ölçmeye çalışmak)if açıklama . Her durumda, bu sorunun istisnalarını kullanmak, çalışma zamanında nesne türünü sorgulamaktır, sadece burada tek boynuzlu at olmayan nesneleri kontrol etmek için dil özelliğini kötüye kullanıyorsunuz. Birif horn > 0


Eski bir bağlamda, if horn > 0bu sorunun başlangıçta çözülme şeklidir. Daha sonra genellikle ortaya çıkan problemler, binicileri ve parıltıları kontrol etmek istediğinizdedir ve horn > 0ilgisiz kodda her yere gömülür (ayrıca, korna 0 olduğunda yapılan kontrollerin eksikliği nedeniyle gizemli hatalardan da muzdariptir). Ayrıca, olaydan sonra alt sınıfı at genellikle en pahalı önerisidir, bu yüzden refactor sonunda hala birlikte kaleme alınmışlarsa, genellikle yapmaya meyilli değilim. Bu yüzden kesinlikle "alternatifler ne kadar çirkin" olur
moarboilerplate

@ moarboilerplate'i kendin söylüyorsun, ucuz ve kolay bir çözüme git ve bir pisliğe dönüşecek. Bu nedenle, OO dilleri bu tür bir soruna bir çözüm olarak icat edildi. alt sınıf at, ilk başta pahalı görünebilir, ancak yakında kendisi için öder. Basit ama çamurlu olan çözüm, zaman içinde daha fazla ve daha pahalıya mal oluyor.
gbjbaanb

3

Sorunun bir functional-programmingetiketi olduğundan, atların iki çeşidini yansıtmak için bir toplam türü ve aralarında netleştirmek için desen eşleştirme özelliğini kullanabiliriz. Örneğin, F # 'da:

type Equine =
| Horse
| Unicorn of hornLength: float

module equines =

  let averageHornLength (equines : Equine list) =
    equines 
    |> List.choose (fun x -> 
      match x with
      | Unicorn u -> Some(u)
      | _ -> None)
    |> List.average

let herd = [ Horse ; Horse ; Unicorn(35.0) ; Horse ; Unicorn(50.0) ]

printfn "Average horn length in herd : %f" (equines.averageHornLength herd) // prints 42.5

FP, OOP üzerinden veri / işlev ayrımı avantajına sahiptir; bu da sizi bir süper tip nesneler listesinden belirli alt tiplere indirirken soyutlama seviyesini ihlal etme (haksız mı?) “Suçlu vicdan” dan kurtarabilir.

Diğer cevaplarda önerilen OO çözümlerinin aksine, desen eşleştirme aynı zamanda Equinebir gün diğer Boynuzlu türlerin ortaya çıkması durumunda daha kolay bir uzama noktası sağlar .


2

Sonunda aynı cevabın kısa şekli kitap veya web makalesi okumayı gerektirir.

Ziyaretçi Deseni

Problem, atların ve tek boynuzlu atların karışımını içeriyor. (Liskov ikame ilkesini ihlal etmek, eski kod tabanlarında yaygın bir sorundur.)

At ve tüm alt sınıflara bir yöntem ekle

Horse.visit(EquineVisitor v)

Equine ziyaretçi arayüzü java / c # ile böyle bir şeye benziyor

interface EquineVisitor {
  void visitHorse(Horse z);
  void visitUnicorn(Unicorn z);
}

Unicorn.visit(EquineVisitor v){
   v.visitUnicorn(this);
}

Horse.visit(EquineVisitor v){
   v.visitHorse(this);
}

Boynuzları ölçmek için şimdi yazıyoruz ....

class HornMeasurer implements EquineVistor {
    void visitHorse(Horse h){} // ignore horses
    void visitUnicorn(Unicorn u){
         double len = u.getHornLength();
         totalLength+=len;
         unicornCount++;
    }

    double getAverageLength(){
          return totalLength/unicornCount;
    }

    double totalLength=0;
    int unicornCount=0;
}

Ziyaretçi deseni yeniden yapılanma ve büyümeyi zorlaştırdığı için eleştiriliyor.

Kısa Cevap: İkili gönderimi almak için Ziyaretçi desen tasarımını kullanın.

ayrıca bakınız https://en.wikipedia.org/wiki/Visitor_pattern

Ayrıca ziyaretçilerin tartışılması için http://c2.com/cgi/wiki?VisitorPattern adresine bakın .

ayrıca Gamma ve ark.


Ziyaretçi kalıbını kendim cevaplamak üzereydim. Daha önce birinden bahsettiğini bulmak için şaşırtıcı bir yol bulmak zorunda kaldım!
Ben Thurley

0

Mimarlığınızda tek boynuzlu atların bir alt tür olduğunu ve Horsebazılarının nerede olabileceğinin bir koleksiyonunu aldığınız yerlerle karşılaştığınızı varsayarak, Unicornkişisel olarak ilk yöntemle gideceğim ( .OfType<Unicorn>()...) çünkü niyetinizi ifade etmenin en basit yolu budur. . Daha sonra gelenler için (3 ay içinde kendiniz de dahil olmak üzere), bu kodla ne yapmaya çalıştığınız hemen bellidir: atlardan tek boynuzlu atları seçin.

Listelenen diğer yöntemler, "Bir tek boynuzlu at mısınız?" Sorusunu sormanın başka bir yolu gibi hissediyorum. Örneğin, bir tür istisna temelli boynuzu ölçmek için bir yöntem kullanırsanız, şuna benzer bir kodunuz olabilir:

foreach (var horse in horses)
{
    try
    {
        var length = horse.MeasureHorn();
        //...
    }
    catch (NoHornException e)
    {
        continue;
    }
}

Yani şimdi istisna, bir şeyin tek boynuzlu at olmadığını gösteriyor. Ve şimdi bu artık istisnai bir durum değil, normal program akışının bir parçası. Ve bir istisna yerine bir istisna kullanmak, ifsadece tip kontrolü yapmaktan çok daha kirli görünüyor.

Diyelim ki atlardaki kornayı kontrol etmek için büyü değerine gidersiniz. Şimdi sınıflarınız şuna benziyor:

class Horse
{
    public double MeasureHorn() { return -1; }
    //...
}

class Unicorn : Horse
{
    public override double MeasureHorn { return _hornLength; }
    //...
}

Şimdi Horsesınıfınız sınıf hakkında bilgi sahibi Unicornolmalı ve umursamadığı şeylerle başa çıkmak için ekstra yöntemlere sahip olmalı. Şimdi, sizden miras kalan Pegasuss ve Zebras'nin de olduğunu hayal edin Horse. Şimdi Horsebir ihtiyacı Flyyanı sıra yöntemi MeasureWings, CountStripesvb Ve sonra Unicornsınıf çok bu yöntemleri alır. Şimdi sınıflarınızın hepsi birbirini tanımak zorunda ve sınıfları "Bu tekboynuzlu at mı?" Diye sormaktan kaçınmak için orada olmaması gereken bir sürü yöntemle kirlettiniz.

Peki bir şey olup Horseolmadığını söylemek için bir şeyler eklemek Unicornve tüm boynuzu ölçmeye ne dersiniz ? Eh, şimdi bir şeyin tek boynuzlu at olup olmadığını bilmek için bu nesnenin varlığını kontrol etmelisiniz (bu sadece bir kontrolün bir başkasının yerine geçmesidir). Aynı zamanda sularda biraz çamurlaşır,List<Horse> unicornsbu gerçekten tek boynuzlu atları tutar, ancak tür sistemi ve hata ayıklayıcı size bunu kolayca söyleyemez. “Ama hepsinin tek boynuzlu at olduğunu biliyorum” diyorsunuz, “adı bile söylüyor”. Peki ya bir şey yanlış adlandırılmışsa? Ya da diyelim ki, gerçekten tek boynuzlu atlar olacağı varsayımıyla bir şeyler yazdınız, ancak daha sonra gereksinimler değişti ve şimdi de pegasi'nin karışmış olabileceğini mi düşünüyorsunuz? (Bunun gibi bir şey asla gerçekleşmez, çünkü özellikle eski yazılım / alaycı.) Artık tip sistemi pegasi'nizi tek boynuzlu atlarınıza yerleştirecektir. Eğer değişkeniniz List<Unicorn>derleyici (veya çalışma ortamı) olarak bildirilmiş olsaydı eğer pegasi veya atlarla karıştırmayı denerseniz uygun olurdu.

Son olarak, tüm bu yöntemler sadece tip sistem kontrolü için bir yedek. Şahsen, ben buradaki tekerleği yeniden icat etmeyi tercih etmemeyi ve kodumun binlerce kez diğer kodlayıcılar tarafından oluşturulmuş ve test edilmiş bir şey kadar iyi çalıştığını umuyorum.

Sonuçta, kodun sizin için anlaşılabilir olması gerekir . Bilgisayar, nasıl yazdığınıza bakılmaksızın çözecektir. Sen hata ayıklamak zorunda ve onun hakkında düşünmek mümkün olan sensin. İşinizi kolaylaştıran seçimi yapın. Herhangi bir sebepten ötürü, bu diğer yöntemlerden biri size göstereceği çift noktadaki daha net koddan daha ağır basan bir avantaj sunuyorsa, bunun için gidin. Ancak bu kod tabanınıza bağlıdır.


Sessiz istisna kesinlikle kötü - benim önerim olacak if(horse.IsUnicorn) horse.MeasureHorn();ve istisnalar yakalanmayacak bir çek oldu - ya !horse.IsUnicornunicorn ölçen bir bağlamda olduğunuzda ya da MeasureHorntek boynuzlu at olmayan bir kişinin içindeyken tetikleneceklerdi . Bu şekilde, istisna atıldığında hataları maskelemezsiniz, tamamen patlar ve bir şeyin düzeltilmesi gereken bir işarettir. Açıkçası, sadece belirli senaryolar için uygun, ancak bir uygulama yolunu belirlemek için istisna atma kullanmayan bir uygulama.
moarboilerplate

0

Anlamsal alan adınızın IS-A ilişkisine sahip olduğu anlaşılıyor, ancak bunu modellemek için alt türler / kalıtım kullanmaktan, özellikle de çalışma zamanı türü yansıması nedeniyle biraz dikkatli olursunuz. Bununla birlikte, yanlış şeyden korktuğunuzu düşünüyorum - altyazı gerçekten tehlikelerle geliyor, ancak çalışma zamanında bir nesneyi sorguladığınız gerçeği sorun değil. Ne demek istediğimi anlayacaksın.

Nesne yönelimli programlama, IS-A ilişkileri nosyonuna oldukça eğildi, tartışmasız olarak çok ciddi bir şekilde eğildi, bu da iki ünlü eleştirel konsepte yol açtı:

Ancak, IS-A ilişkilerine bakmanın belki de bu güçlükleri olmayan başka bir işlevsel programlama tabanlı başka bir yol olduğunu düşünüyorum. İlk olarak, biz bir yaşayacaksın yüzden, bizim programda atlar ve tek boynuzlu modellemek istiyoruz Horseve bir Unicorntür. Bu türlerin değerleri nelerdir? Şunu söyleyebilirim ki:

  1. Bu türlerin değerleri (sırasıyla) atların ve tek boynuzlu atların temsilleri veya açıklamalarıdır ;
  2. Bunlar şematik gösterimler veya açıklamalardır; serbest biçimli değillerdir, çok katı kurallara göre inşa edilmişlerdir.

Kulağa açık gelebilir, ancak bence insanların daire elips sorunu gibi konulara girme yollarından biri, bu noktalara yeterince dikkat etmeyerek. Her daire bir elipstir, ancak bu, bir dairenin her şematik tanımının otomatik olarak bir elipsin farklı bir şemaya göre şematik bir açıklaması olduğu anlamına gelmez. Başka bir deyişle, bir dairedir sırf bir elips bir anlamına gelmez Circlebir olduğunu Ellipse, tabiri caizse. Ancak bu demek oluyor ki:

  1. Bir bulunmaktadır eden toplam fonksiyon bir dönüştürür Circlebir halinde (şematik daire açıklama) Ellipseaynı daireleri anlatan (bilgi farklı tip);
  2. Bir daire tanımlayan ve karşılık gelen döndüren bir kısmi fonksiyon vardır .EllipseCircle

Bu nedenle, işlevsel programlama terimlerinde, Unicorntürünüzün bir alt tür olması gerekmez Horse, sadece aşağıdaki gibi işlemlere ihtiyacınız vardır:

-- Convert any unicorn-description of into a horse-description that
-- describes the same unicorns.
toHorse :: Unicorn -> Horse

-- If the horse described by the given horse-description is a unicorn,
-- then return a unicorn-description of that unicorn, otherwise return
-- nothing.
toUnicorn :: Horse -> Maybe Unicorn

Ve toUnicornbunun tam tersi olması gerekir toHorse:

toUnicorn (toHorse x) = Just x

Haskell'in Maybetürü, diğer dillerin "seçenek" türü dedikleri şeydir. Örneğin, Java 8 Optional<Unicorn>tipi ya bir Unicornya hiçdir. Alternatiflerinizden ikisinin (bir istisna atma veya "varsayılan veya sihirli bir değer" döndürme) seçenek türlerine çok benzer olduğunu unutmayın.

Temel olarak burada yaptığım şey, alt tipler veya kalıtım kullanmadan, IS-A ilişkisini türler ve işlevler açısından yeniden yapılandırmaktır. Bundan uzak tutacağım şey:

  1. Modelinizin bir Horsetürü olması gerekiyor ;
  2. HorseTürü ihtiyacı olan herhangi bir değer, bir tek boynuzlu tarif olup açıkça belirlemek için yeterli bilgi kodlamak için;
  3. Türün bazı işlemlerinin, bu Horsetür bilgileri Horseistemesi için belirli bir unicorn olup olmadığını gözlemleyebilmesi için bu tür bilgileri açığa çıkarması gerekir ;
  4. Türün istemcileri, Horsebu sonraki işlemleri tek boynuzlu atlar ve atlar arasında ayrım yapmak için çalışma zamanında kullanmak zorunda kalacak.

Yani bu temelde bir " Horsetek boynuzlu at olup olmadığını sormak " bir modeldir. Sen bu modele karşı temkinlisin, ama bence yanlış. Size bir Horses listesi verirseniz , söz konusu olanın garanti ettiği şey, listedeki öğelerin tanımladığı şeylerin atlar olduğudur - kaçınılmaz olarak, çalışma zamanında hangisinin tek boynuzlu olduğunu söylemek için bir şeyler yapmanız gerekir. Dolayısıyla, bunun üstesinden gelmek yok, bence - sizin için yapacak işlemleri yapmanız gerekiyor.

Nesne yönelimli programlamada, bunu yapmanın bilinen yolu şudur:

  • Bir Horsetür var;
  • Var Unicornbir alt tipi olarak Horse;
  • Belirli Horsebir zaman olup olmadığını belirleyen, istemci tarafından erişilebilir bir işlem olarak çalışma zamanı türü yansımasını kullanın Unicorn.

Bu, yukarıda sunduğum "şey ve açıklama" açısından bakarken, çok büyük bir zayıflık var:

  • Tek Horseboynuzlu atı tarif eden, ancak bir Unicornörnek olmayan bir örneğiniz varsa?

En başa dönersek, bu bir IS-A ilişkisini modellemek için alt yazı ve alt pencereleri kullanmanın gerçekten korkutucu bir parçası olduğunu düşünüyorum - bir çalışma zamanı kontrolü yapmanız gerekmiyor. Tipografiyi biraz kötüye kullanmak, Horsebunun bir Unicornörnek olup olmadığını sormak Horse, tek boynuzlu at olup olmadığını sormakla eşanlamlı değildir ( Horseaynı zamanda tek boynuzlu at olan bir atın tanımı olup olmadığı ). Programınız, bir kodu tekboynuzlu atı tarif Horseseden bir müşteri kurmaya çalıştığında yapılandıran kodu kapatacak kadar uzun Horsesürmediyse, Unicornsınıf başlatılır. Tecrübelerime göre, nadiren programcılar bunu dikkatlice yaparlar.

Bu yüzden, Horses'yi Unicorns'ye çeviren açık ve aşağı olmayan bir işlemin olduğu yaklaşıma giderdim . Bu, aşağıdaki Horsetürden bir yöntem olabilir :

interface Horse {
    // ...
    Optional<Unicorn> toUnicorn();
}

... ya da harici bir nesne olabilir ("atın tek boynuzlu at olup olmadığını söyleyen bir at üzerindeki ayrı nesnen"):

class HorseToUnicornCoercion {
    Optional<Unicorn> convert(Horse horse) {
       // ...
    }
}

Bunların arasında seçim yapmak, programınızın nasıl organize edildiğine ilişkin bir konudur - her iki durumda da, işlemimin Horse -> Maybe Unicornyukarıdan eşdeğeri vardır, sadece farklı şekillerde paketliyorsunuzdur (kuşkusuz bu tür işlemlerin ne tür işlemler Horsegerektirdiği üzerinde dalgalanma etkileri olacaktır. müşterilerine göstermek için).


-1

OP'nin başka bir cevaba yaptığı yorum soruyu açıkladı, diye düşündüm

bu da sorunun ne sorduğunun bir parçası. Eğer bir at sürüsüm varsa ve bazıları kavramsal olarak tek boynuzlu atlarsa, sorunun çok fazla olumsuz etki olmadan temiz bir şekilde çözülebilmesi için nasıl var olmaları gerekir?

Bu şekilde ifade edildiğinde, daha fazla bilgiye ihtiyacımız olduğunu düşünüyorum. Cevap muhtemelen bir çok şeye bağlıdır:

  • Dil tesislerimiz. Örneğin, muhtemelen bu yakut ve javascript ve Java ile farklı yaklaşırdım.
  • Kavramlar kendileri: Ne olduğu bir at ve hangi olan tek boynuzlu bir at? Hangi veriler birbiriyle ilişkili? Boynuz hariç tam olarak aynı mı, yoksa başka farkları var mı?
  • Boynuz uzunluğu ortalamalarını almanın dışında, bunları başka nasıl kullanıyoruz? Peki ya sürüler? Belki onları da modellemeliyiz? Onları başka bir yerde mi kullanıyoruz? herd.averageHornLength()kavramsal modelimize uyuyor gibi görünüyor.
  • At ve tek boynuzlu at nesneleri nasıl yaratılıyor? Bu kodu refactoring'in sınırları dahilinde değiştirmek mi?

Yine de genel olarak, burada kalıtım ve alt türler hakkında düşünmezdim. Nesnelerin bir listesi var. Bu nesnelerden bazıları, belki de bir hornLength()yöntemleri olduğundan tek boynuzlu atlar olarak tanımlanabilir . Bu tek boynuzlu at-benzersiz özelliğine göre listeyi filtreleyin. Şimdi sorun tek boynuzlu atların bir listesinin boynuz uzunluğunun ortalamasına indirgenmiştir.

OP, hala yanlış anlıyorsam bana haber ver.


1
Fuar puanları. Sorunun daha soyut hale gelmemesi için bazı makul varsayımlar yapmalıyız: 1) kuvvetle yazılmış bir dil 2) sürü, atları bir türle sınırlar, muhtemelen koleksiyon nedeniyle; 3) ördek yazma gibi tekniklerden kaçınılmalıdır. . Neyin değişebileceğine gelince, herhangi bir sınırlama yoktur, ancak her değişikliğin kendine has sonuçları vardır ...
saat

Sürü atları bir türle sınırlarsa, tek seçenek mirasımız (bu seçeneği sevmiyoruz) veya HerdMemberat veya tek boynuzlu atla (atı ve tek boynuzlu atı serbest bırakmak için alt tür ilişkilerine ihtiyaç duymaktan kurtulacağımız tek seçenek mirasımız değil mi? ). HerdMembersonra isUnicorn()uygun gördüğü halde uygulamakta serbesttir ve önerideki filtreleme çözümü bunu izler.
Jonah

Bazı dillerde, hornLength () karıştırılabilir ve bu durumda geçerli bir çözüm olabilir. Bununla birlikte, yazmanın daha az esnek olduğu dillerde, aynı şeyi yapmak için bazı çürütme tekniklerine başvurmanız ya da atta kıkırdama yaratabileceği bir ata boynuzlu koyma gibi bir şey yapmanız gerekir. t Kavramsal olarak boynuzları olabilir. Ayrıca, matematiksel hesaplamalar yaparsanız, varsayılan değerler de dahil olmak üzere sonuçları
eğrilebilir

Yine de, karışımlar çalışma süreleri olmadıkça, başka bir ad altında miras kalırlar. "Bir atın kavramsal olarak boynuzları yok" yorumunuz, ne olduğumuz hakkında daha fazla şey bilmemiz gerektiği konusundaki yorumumla, cevabımızın atları ve tek boynuzlu atları modellememizi ve birbirleriyle olan ilişkisini de içermesi gerekiyorsa yorumumla ilgili. Varsayılan değerleri içeren herhangi bir çözüm el ile yanlış imo.
Jonah

Bu sorunun belirli bir tezahürü için kesin bir çözüm elde etmek konusunda haklısınız, çok fazla içeriğe sahip olmanız gerekiyor. Boynuzlu bir atla ilgili sorunuzu yanıtlamak ve onu mikslere bağlamak için, boynuz uzunluğunun tek boynuzlu at olmayan bir ata karıştığı bir senaryo düşünüyordum. Bir istisna atan hornLength için varsayılan bir uygulaması olan bir Scala özelliğini göz önünde bulundurun. Tek boynuzlu at türü bu uygulamayı geçersiz kılabilir ve eğer bir at bunu hornLength değerinin değerlendirildiği bir bağlamda yaparsa, bu bir istisnadır.
moarboilerplate

-2

Bir IEnumerable döndüren GetUnicorns () yöntemi bana en şık, esnek ve evrensel bir çözüm gibi görünüyor. Bu şekilde, atın sadece sınıf tipi veya belirli bir mülkün değeri değil, tek boynuzlu at olarak geçip geçmeyeceğini belirleyen herhangi bir özellik ile başa çıkabilirsiniz.


Buna katılıyorum. Mason Wheeler'ın cevabında da iyi bir çözümü var, ancak tek boynuzlu atları farklı yerlerde farklı nedenlerle ayırmanız gerekiyorsa, kodunuzun birçok kurumu olacak horses.ofType<Unicorn>.... Bir GetUnicornsişleve sahip olmak tek bir astar olacaktır, ancak at / tek boynuzlu at ilişkisindeki değişikliklere arayanın bakış açısından daha dirençli olacaktır.
Shaz

@Ryan, eğer IEnumerable<Horse>tek boynuzlu at kriterleriniz tek bir yerde olsa da, bir geri dönerseniz , kapsüllenmiştir, bu nedenle arayanlar neden tek boynuzlu atlara ihtiyaç duyduklarına dair varsayımlarda bulunmalıdırlar (bugün çorbayı sipariş ederek istiridye çorbasını alabilirim, ama bu yapmaz ' demek istediğim yarın aynı şeyi yaparak alacağım). Ayrıca, üzerindeki bir korna için varsayılan bir değer göstermelisiniz Horse. Eğer Unicornkendi türüdür, sen havai tanıtabilirsiniz tipi eşleştirmeleri, yeni bir tür oluşturmak ve korumak zorunda.
moarboilerplate

1
@moarboilerplate: Bunların hepsini çözüme destek veriyoruz. Güzelliği, tek boynuzlu atın herhangi bir uygulama detayından bağımsız olmasıdır. Bir veri üyesine, sınıfa veya günün saatine göre ayırımcılık yapsanız da (ayın tüm bildiklerime uygun olması durumunda bu atlar gece yarısı tek boynuzlu atlara dönüşebilir), çözüm duruyor, arayüz aynı kalıyor.
Martin Maat
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.