Tartışma sayıları düşük tutulur ve üçüncü taraf bağımlılıkları nasıl ayrı tutulur?


13

Üçüncü taraf bir kütüphane kullanıyorum. Bana niyetlerimiz ve amaçlarımız için muhtemelen şu şekilde uygulanan bir POJO verdiler :

public class OurData {
  private String foo;
  private String bar;
  private String baz;
  private String quux;
  // A lot more than this

  // IMPORTANT: NOTE THAT THIS IS A PACKAGE PRIVATE CONSTRUCTOR
  OurData(/* I don't know what they do */) {
    // some stuff
  }

  public String getFoo() {
    return foo;
  }

  // etc.
}

API'lerini kapsüllemek ve birim testini kolaylaştırmak dahil ancak bunlarla sınırlı olmamak üzere birçok nedenden dolayı, verilerini sarmak istiyorum. Ama çekirdek sınıflarımın verilere bağımlı olmasını istemiyorum (yine test nedenleriyle)! Şu anda böyle bir şey var:

public class DataTypeOne implements DataInterface {
  private String foo;
  private int bar;
  private double baz;

  public DataTypeOne(String foo, int bar, double baz) {
    this.foo = foo;
    this.bar = bar;
    this.baz = baz;
  }
}

public class DataTypeTwo implements DataInterface {
  private String foo;
  private int bar;
  private double baz;

  public DataTypeOne(String foo, int bar, double baz, String quux) {
    this.foo = foo;
    this.bar = bar;
    this.baz = baz;
    this.quux = quux;
  }
}

Ve sonra bu:

public class ThirdPartyAdapter {
  public static makeMyData(OurData data) {
    if(data.getQuux() == null) {
      return new DataTypeOne(
        data.getFoo(),
        Integer.parseInt(data.getBar()),
        Double.parseDouble(data.getBaz()),
      );
    } else {
      return new DataTypeTwo(
        data.getFoo(),
        Integer.parseInt(data.getBar()),
        Double.parseDouble(data.getBaz()),
        data.getQuux();
      );
  }
}

Bu bağdaştırıcı sınıfı, üçüncü taraf API hakkında bilmeniz gereken diğer birkaç sınıfla birleştirilir, bu da sistemimin geri kalanı boyunca yaygınlığını sınırlar. Ancak ... bu çözüm BRÜT! Temiz Kod'da, sayfa 40:

Üçten fazla argüman (çok yönlü) çok özel bir gerekçe gerektirir - ve daha sonra yine de kullanılmamalıdır.

Düşündüğüm şeyler:

  • Statik yardımcı yöntem yerine fabrika nesnesi oluşturma
    • Bajillion argümanlarına sahip olma sorununu çözmez
  • Bağımlı bir kurucuya sahip bir DataTypeOne ve DataTypeTwo alt sınıfı oluşturma
    • Hala çok yönlü korumalı bir kurucu var
  • Aynı arabirime uyan tamamen ayrı uygulamalar oluşturun
  • Yukarıdaki fikirlerden birden fazlası aynı anda

Bu durum nasıl ele alınmalı?


Bunun bir yolsuzlukla mücadele katmanı durumu olmadığını unutmayın . API'lerinde yanlış bir şey yok. Sorunlar:

  • Veri yapılarımın olmasını istemiyorum import com.third.party.library.SomeDataStructure;
  • Test yapılarımda veri yapılarını oluşturamıyorum
  • Mevcut çözümüm çok yüksek argüman sayımları ile sonuçlanıyor. Veri yapılarına geçmeden argüman sayılarını düşük tutmak istiyorum.
  • Bu soru "olduğunu Ne bir anti-yolsuzluk tabakasıdır?". Benim sorum, " bu senaryoyu çözmek için bir deseni, herhangi bir deseni nasıl kullanabilirim?"

Ben de kod istemiyorum, ya da (aksi takdirde bu soru SO olurdu), sadece yeterli kod (bu soru sağlamaz) yazmak için etkinleştirmek için yeterli bir cevap istiyor.


Böyle üçüncü taraf POJO'ları varsa, test kurallarınız olarak bazı kurallara sahip bir Harita kullanan (örneğin, int_bar anahtarlarını adlandırın) özel test kodu yazmak için çaba göstermeye değer olabilir. Veya bazı özel ara kodlarla JSON veya XML kullanın. Aslında, com.thirdparty'yi test etmek için bir çeşit DSL.
user949300

Temiz The ideal number of arguments for a function is zero (niladic). Next comes one (monadic), followed closely by two (dyadic). Three arguments (triadic) should be avoided where possible. More than three (polyadic) requires very special justification — and then shouldn’t be used anyway.
Lilienthal

11
Bir desene veya programlama kılavuzuna kör bağlılık, kendi anti-paternidir .
Lilienthal

2
"API'lerini kapsüllemek ve birim testini kolaylaştırmak" Bunun gibi sesler, benim için aşırı test ve / veya Test kaynaklı Tasarım Hasarı (veya başlamak için farklı şekilde tasarlayabileceğinizin göstergesi) olabilir. Kendinize şunu sorun: Bu , kodunuzu anlamayı, değiştirmeyi ve yeniden kullanmayı gerçekten kolaylaştırıyor mu? Paramı "hayır" a koyardım. Bu kütüphaneyi hiç değiştirmemeniz ne kadar gerçekçi? Muhtemelen çok değil. Eğer takas yaparsanız, bu tamamen farklı olanı yerine bırakmayı gerçekten kolaylaştırır mı? Yine, "hayır" üzerine bahse girerim.
jpmc26

1
@JamesAnderson İlginç olanı bulduğum için teklifin tamamını yeniden oluşturdum, ancak snippet'ten genel olarak veya özel olarak yapıcılara atıfta bulunup bulunmadığı net değildi. Hak talebini onaylamak istemedim ve jpmc26'nın dediği gibi, bir sonraki yorumum size bunu yapmadığımı gösteren bazı işaretler vermelidir. Neden akademisyenlere saldırmaya ihtiyaç duyduğunuzdan emin değilim ama polisillables kullanmak, birini bulutların üzerindeki fildişi kulesine tünemiş bir akademik elitist yapmıyor.
Lilienthal

Yanıtlar:


10

Birkaç başlatma parametresi olduğunda kullandığım strateji, yalnızca başlatma parametrelerini içeren bir tür oluşturmaktır

public class DataTypeTwoParameters {
    public String foo;  // use getters/setters instead if it's appropriate
    public int bar;
    public double baz;
    public String quuz;
}

Sonra DataTypeTwo yapıcısı bir DataTypeTwoParameters nesnesi alır ve DataTypeTwo şu yolla oluşturulur:

DataTypeTwoParameters p = new DataTypeTwoParameters();
p.foo = "Hello";
p.bar = 4;
p.baz = 3;
p.quuz = "World";

DataTypeTwo dtt = new DataTypeTwo(p);

Bu, DataTypeTwo'ya giren tüm parametrelerin ne olduğunu ve ne anlama geldiğini netleştirmek için çok fazla fırsat verir. Ayrıca, DataTypeTwoParameters yapıcısında mantıklı varsayılanlar da sağlayabilirsiniz, böylece yalnızca ayarlanması gereken değerler API'nin tüketicisinin beğeneceği herhangi bir sırada yapılabilir.


İlginç bir yaklaşım. Alakalı nereye koyardınız Integer.parseInt? Bir ayarlayıcıda mı yoksa parametreler sınıfının dışında mı?
durron597

5
Parametreler sınıfının dışında. Parametreler sınıfı "aptal" bir nesne olmalı ve gerekli girdilerin ve türlerinin ne olduğunu ifade etmekten başka bir şey yapmaya çalışmamalıdır. Ayrıştırma gibi, başka bir yerde yapılmalıdır: p.bar = Integer.parseInt("4").
Erik

7
Bu bir Parametre Nesnesi deseni gibi geliyor
gnat

9
... veya anti-desen.
Telastyn

1
... ya da sadece adlandırmak olabilir DataTypeTwoParametersiçin DataTypeTwo.
user253751

14

Burada gerçekten iki ayrı endişeniz var: bir API'yı sarmalamak ve bağımsız değişken sayısını düşük tutmak.

Bir API'yi sararken fikir, arayüzü sıfırdan sanki gereksinimlerden başka bir şey bilmeden tasarlamaktır. Test edilebilirlik, inşa edilebilirlik, bir nesne içinde çok fazla parametreleri vb API yazın: Sen yanlış bir şey onların API ile yanlış şeylerin bir sayı daha sonra aynı nefes listesinde, onların API ile olduğunu söylüyor dilek sen vardı. Bu, bunun yerine birden çok nesne gerektiriyorsa, bunu yapın. POJO'yu oluşturan nesnelere bir düzey daha yukarı kaydırılması gerekiyorsa , bunu yapın.

Ardından, istediğiniz API'yı aldıktan sonra parametre sayısı artık sorun olmayabilir. Öyleyse, dikkate alınması gereken birkaç ortak desen vardır:

  • Erik'in cevabında olduğu gibi bir parametre nesnesi .
  • Oluşturucu deseni ayrı bir kurucu obje yaratmak, daha sonra tek tek parametrelerini ayarlamak bitiş nesnesi oluşturmak için belirleyici Ağustos numarayı aramak.
  • Prototip model alanlar zaten içten set ile, istediğiniz nesnenin alt sınıfları klonlamak.
  • Zaten aşina olduğunuz bir fabrika.
  • Yukarıdakilerin bir kombinasyonu.

Bu yaratıcı modellerin genellikle, kapsüllendiğinde iyi olduğunu düşünmeniz gereken bir polyadik kurucu çağırdığını unutmayın. Polyadik kurucuları ile ilgili sorun onları bir kez çağırmıyor, bir nesne inşa etmek istediğiniz her seferinde onları çağırmak zorunda kalıyorsunuz.

OurDataNesneye bir referans depolayarak ve yöntem çağrılarını ileterek, dahili bileşenlerini yeniden uygulamaya çalışmak yerine genellikle temel API'ya geçmenin çok daha kolay ve daha sürdürülebilir olduğunu unutmayın . Örneğin:

public class DataTypeTwo implements DataInterface {
  private OurData data;

  public DataTypeOne(OurData data) {
    this.data = data;
  }

   public String getFoo() {
    return data.getFoo();
  }

  public int getBar() {
    return Integer.parseInt(data.getBar());
  }
  ...
}

Bu cevabın ilk yarısı: harika, çok yardımcı, +1. Bu cevabın ikinci yarısı: " OurDataNesneye bir referans depolayarak temel API'ya geç " - bu, en azından temel sınıfta, bağımlılık olmadığından emin olmak için kaçınmaya çalışıyorum.
durron597

1
Bu yüzden bunu yalnızca uygulamalarınızdan birinde yaparsınız DataInterface. Sahte nesneleriniz için başka bir uygulama oluşturursunuz.
Karl Bielefeldt

@ durron597: evet, ama sizi gerçekten rahatsız ediyorsa bu sorunu nasıl çözeceğinizi zaten biliyorsunuz.
Doc Brown

1

Sanırım Bob Amca'nın tavsiyesini çok sıkı yorumluyor olabilirsiniz. Normal sınıflar için, mantık ve yöntemler ve yapıcılar ve benzerleri ile, çok yönlü bir kurucu gerçekten de kod kokusuna çok benzer. Ancak, kesinlikle alanları gösteren bir veri kabı olan ve aslında bir Fabrika nesnesi tarafından üretilen bir şey için, bunun çok kötü olduğunu düşünmüyorum.

Sen edebilirsiniz yorumunda önerildiği gibi olan sarıcı yerel veri türü ne olduğunu sizin için bu yapıcı parametreleri sarmalayabilirsiniz, Parametre Nesne deseni kullanmak zaten , esasen, bir parametre nesnesi. Tüm Parameter nesneniz yapacak olan parametreler yukarıya (Polyadik bir kurucu ile nasıl yaratacaksınız?) Ve bir saniye sonra bunları neredeyse aynı olan bir nesneye açmaktır.

Alanlarınız için ayarlayıcıları ortaya çıkarmak ve onları çağırmak istemiyorsanız, iyi tanımlanmış ve kapsüllenmiş bir fabrikada çok yönlü bir kurucuya bağlı kalmanın iyi olduğunu düşünüyorum.


Sorun şu ki, veri yapımdaki alan sayısı birden çok kez değişti ve muhtemelen tekrar değişecek. Bu, tüm test durumlarımda kurucuyu yeniden düzenlemem gerektiği anlamına gelir. Varsayılan değerleri olan parametre düzeni daha iyi bir yol gibi geliyor; değişmez forma kaydedilen değişebilir bir versiyona sahip olmak hayatımı birçok yönden kolaylaştırabilir.
durron597
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.