Hangisi sadece bir arabirimi uygulayan, diğerini değil bir sınıfı kullanan bir yöntemi çağırmanın en iyi yoludur?


11

Temelde belirli bir koşul verildiğinde farklı eylemler yürütmem gerekiyor. Mevcut kod bu şekilde yazılır

Temel arayüz

// DoSomething.java
interface DoSomething {

   void letDoIt(String info);
}

Birinci işçi sınıfının uygulanması

class DoItThisWay implements DoSomething {
  ...
}

İkinci işçi sınıfının uygulanması

class DoItThatWay implements DoSomething {
   ...
}

Ana sınıf

   class Main {
     public doingIt(String info) {
        DoSomething worker;
        if (info == 'this') {
          worker = new DoItThisWay();
        } else {
          worker = new DoItThatWay();
        }
        worker.letDoIt(info)
     }

Bu kod iyi çalışıyor ve anlaşılması kolaydır.

Şimdi, yeni bir gereksinim nedeniyle, sadece mantıklı olan yeni bir bilgi geçmem gerekiyor DoItThisWay.

Benim sorum: Aşağıdaki kodlama tarzı bu gereksinimi karşılamak için iyi yapar.

Yeni sınıf değişkeni ve yöntemini kullan

// Use new class variable and method

class DoItThisWay implements DoSomething {
  private int quality;
  DoSomething() {
    quality = 0;
  }

  public void setQuality(int quality) {
    this.quality = quality;
  };

 public void letDoIt(String info) {
   if (quality > 50) { // make use of the new information
     ...
   } else {
     ...
   }
 } ;

}

Bu şekilde yaparsam, arayan için ilgili değişikliği yapmak gerekir:

   class Main {
     public doingIt(String info) {
        DoSomething worker;
        if (info == 'this') {
          int quality = obtainQualityInfo();
          DoItThisWay tmp = new DoItThisWay();
          tmp.setQuality(quality)
          worker = tmp;

        } else {
          worker = new DoItThatWay();
        }
        worker.letDoIt(info)
     }

İyi bir kodlama tarzı mı? Yoksa sadece atabilir miyim

   class Main {
     public doingIt(String info) {
        DoSomething worker;
        if (info == 'this') {
          int quality = obtainQualityInfo();
          worker = new DoItThisWay();
          ((DoItThisWay) worker).setQuality(quality)
        } else {
          worker = new DoItThatWay();
        }
        worker.letDoIt(info)
     }

19
Neden sadece geçemedi qualityiçin yapıcı içine DoItThisWay?
David Arno

Muhtemelen sorumu gözden geçirmem gerekiyor ... çünkü performans nedeni ile inşaatının yapımcısı DoItThisWayve bir DoItThatWaykez yapılır Main. Mainuzun yaşayan bir sınıftır ve doingItdefalarca defalarca çağrılır.
Anthony Kong

Evet, bu durumda sorunuzu güncellemek iyi bir fikir olacaktır.
David Arno

setQualityYöntemin DoItThisWaynesnenin ömrü boyunca birden çok kez çağrılacağını mı söylemek istiyorsun ?
jpmc26

1
Sunulan iki sürüm arasında anlamlı bir fark yok. Mantıksal bir bakış açısından, aynı şeyi yaparlar.
Sebastian Redl

Yanıtlar:


6

Ben bir qualityher letDoIt()arama yanında ayarlanması gerektiğini varsayıyorum DoItThisWay().

Burada ortaya çıkan bkz mesele şudur: Sen tanıtıyoruz zamansal bağlantı (yani aramak unutursanız ne happends setQuality()çağırmadan önce letDoIt()bir üzerinde DoItThisWay?). Ve yönelik uygulamalar DoItThisWayve DoItThatWaysapan ve (bir ihtiyaçları olduğu setQuality()diğer değil, denir).

Bu şu anda sorunlara neden olmasa da, sonunda size musallat olmak için geri gelebilir. Başka bir göz atmaya letDoIt()ve kalite bilgisinin infoiçinden geçtiğiniz bir parçası olması gerekip gerekmediğine bakmaya değer olabilir ; ancak bu ayrıntılara bağlıdır.


15

İyi bir kodlama tarzı mı?

Benim bakış açımdan, sürümlerinizden hiçbiri değil. İlk aramak zorunda setQualityönce letDoItçağrılabilir bir olan zamansal bağlantı . DoItThisWayBir türevi olarak görmeye sıkıştınız DoSomething, ancak (en azından işlevsel olarak değil), daha çok

interface DoSomethingWithQuality {
   void letDoIt(String info, int quality);
}

Bu daha Mainçok

class Main {
    // omitting the creation
    private DoSomething doSomething;
    private DoSomethingWithQuality doSomethingWithQuality;

    public doingIt(String info) {
        DoSomething worker;
        if (info == 'this') {
            int quality = obtainQualityInfo();
            doSomethingWithQuality.letDoIt(info, quality);
        } else {
            doSomething.letDoIt(info);
        }
    }
}

Öte yandan, parametreleri doğrudan sınıflara geçirebilirsiniz (bunun mümkün olduğunu varsayarak) ve hangisinin bir fabrikaya kullanılacağına karar verebilirsiniz (bu, örnekleri tekrar değiştirilebilir hale getirir ve her ikisi de türetilebilir DoSomething). Bu şöyle Maingörünecektir

class Main {
    private DoSomethingFactory doSomethingFactory;

     public doingIt(String info) {
         int quality = obtainQualityInfo();
         DoSomething doSomethingWorker = doSomethingFactory.Create(info, quality);
         doSomethingWorker.letDoIt();
     }
}

Bunu yazdığının farkındayım

çünkü performans nedeniyle DoItThisWay ve DoItThatWay'in yapımı bir kez Main'in yapıcısında yapılır.

Ancak, fabrikada oluşturulması pahalı olan parçaları önbelleğe alabilir ve bunları yapıcıya da aktarabilirsiniz.


Argh, cevapları yazarken beni gözetliyor musun? Böyle benzer noktalar yapıyoruz (ama seninki çok daha kapsamlı ve etkili);)
CharonX

1
@DocBrown Evet, tartışmalıdır, ancak DoItThisWayaranmadan önce kalitenin ayarlanması gerektiğinden letDoItfarklı davranır. DoSomethingTüm türevler için aynı şekilde çalışmasını beklerdim (daha önce kaliteyi belirlemez). Bu mantıklı mı? Tabii ki bu biraz bulanık, çünkü eğer setQualitybir fabrika yönteminden çağırırsak, müşteri kalitenin ayarlanması gerekip gerekmediği konusunda agnostik olacaktır.
Paul Kertscher

2
@DocBrown Ben letDoIt()(bana herhangi bir ek bilgi eksik) sözleşme varsayalım "Ben bana sağlarsanız doğru yürütmek (ne yaparsam yapın) String info." setQuality()Önceden çağrılmak istenmesi , çağrının ön koşulunu güçlendirir letDoIt()ve bu nedenle LSP'nin ihlali olacaktır.
CharonX

2
@CharonX: Örneğin, " letDoIt" herhangi bir istisna atmayacağını ve "setQuality" olarak adlandırmayı unutmayı letDoItbir istisna haline getirirse , böyle bir uygulamanın LSP'yi ihlal edeceğini kabul eden bir sözleşme yapılırsa . Ama bunlar benim için çok yapay varsayımlara benziyor.
Doc Brown

1
Bu iyi bir cevap. Ekleyeceğim tek şey, geçici bir eşleşmenin kod tabanı için ne kadar fena halde kötü olduğuna dair bazı yorumlar. Görünüşte birbirinden ayrılmış bileşenler arasında gizli bir sözleşme. Normal kuplaj ile, en azından açık ve kolayca tanımlanabilir. Bunu yapmak aslında bir çukuru kazmak ve yapraklarla kaplamaktır.
JimmyJames

10

Diyelim ki yapıcıya qualityaktarılamıyor ve çağrı setQualitygerekiyor.

Şu anda aşağıdaki gibi bir kod pasajı

    int quality = obtainQualityInfo();
    worker = new DoItThisWay();
    ((DoItThisWay) worker).setQuality(quality);

ona çok fazla düşünce yatırmak için çok küçük. IMHO biraz çirkin gözüküyor, ama anlaşılması zor değil.

Böyle bir kod snippet'i büyüdüğünde ve yeniden düzenleme sayesinde,

    int quality = obtainQualityInfo();
    worker = CreateAWorkerForThisInfo();
    ((DoItThisWay) worker).setQuality(quality);

Şimdi, bu kodun hala doğru olup olmadığını hemen görmüyorsunuz ve derleyici size söylemiyor. Bu nedenle, doğru tipte geçici bir değişken getirmek ve dökümden kaçınmak, gerçek bir ekstra çaba harcamadan biraz daha güvenlidir.

Ancak, aslında tmpdaha iyi bir isim verirdim:

      int quality = obtainQualityInfo();
      DoItThisWay workerThisWay = new DoItThisWay();
      workerThisWay.setQuality(quality)
      worker = workerThisWay;

Bu adlandırma, yanlış kodun yanlış görünmesine yardımcı olur .


tmp "workerThatUsesQuality" gibi bir şey daha iyi adlandırılmış olabilir
user949300 9

1
@ user949300: IMHO bu iyi bir fikir değil: DoItThisWaydaha spesifik parametreler aldığını varsayalım - önerdiğinize göre, adın her seferinde değiştirilmesi gerekiyor. Hangi yöntem adlarının kullanılabilir olmadığını, nesne türünü netleştiren değişkenler veya adlar kullanmak daha iyidir.
Doc Brown

Açıklığa kavuşturmak için, değişkenin adı olurdu, sınıfın adı değil. Tüm yetenekleri sınıf adına koymanın kötü bir fikir olduğunu kabul ediyorum. Bu yüzden bunun böyle bir workerThisWayşey olmasını öneriyorum usingQuality. Bu küçük kod snippet'inde daha spesifik olmak daha güvenlidir. (Ve, etki alanlarında "usingQuality" ifadesinden daha iyi bir ifade varsa bunu kullanın.
user949300

2

Başlatma süresinin çalışma zamanı verilerine bağlı olarak değiştiği ortak bir arabirim genellikle kendini fabrika modeline verir.

DoThingsFactory factory = new DoThingsFactory(thingThatProvidesQuality);
DoSomething doSomething = factory.getDoer(info);

doSomething.letDoIt();

Daha sonra DoThingsFactory getDoer(info), DoSomething arayüzüne dökülen somut DoThingsWithQuality nesnesini döndürmeden önce kalite bilgisini yöntem içinde alma ve ayarlama konusunda endişelenebilir .

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.