Ziyaretçi desenini büyük nesne hiyerarşisiyle kullanma


12

bağlam

Nesnelerin bir hiyerarşisi (bir ifade ağacı) bir "sözde" ziyaretçi desen (sözde, çift gönderme kullanmaz gibi) ile kullanıyorum:

 public interface MyInterface
 {
      void Accept(SomeClass operationClass);
 }

 public class MyImpl : MyInterface 
 {
      public void Accept(SomeClass operationClass)
      {   
           operationClass.DoSomething();
           operationClass.DoSomethingElse();
           // ... and so on ...
      }
 }

Ancak, MyInterface'in uygulama sayısı önemli (~ 50 veya daha fazla) olduğundan ve fazladan işlem eklemem gerekmediğinden, bu tasarım tartışmalı, oldukça rahattı.

Her uygulama benzersizdir (farklı bir ifade veya işleç) ve bazıları kompozitlerdir (diğer işleç / yaprak düğümlerini içerecek işleç düğümleri).

Geçiş şu anda ağacın kök düğümündeki Accept işlemini çağırarak gerçekleştirilir, bu da sırayla alt düğümlerinin her birinde Accept'i çağırır ...

Ama güzel baskı gibi yeni bir işlem eklemem gereken zaman geldi :

 public class MyImpl : MyInterface 
 {
      // Property does not come from MyInterface
      public string SomeProperty { get; set; }

      public void Accept(SomeClass operationClass)
      {   
           operationClass.DoSomething();
           operationClass.DoSomethingElse();
           // ... and so on ...
      }

      public void Accept(SomePrettyPrinter printer)
      {
           printer.PrettyPrint(this.SomeProperty);
      }
 }    

Temel olarak iki seçenek görüyorum:

  • Aynı tasarımı koruyarak, her türetilmiş sınıfa operasyonum için yeni bir yöntem ekleyerek, bakım pahasına (bir seçenek değil, IMHO)
  • Genişletilebilirlik pahasına "gerçek" Ziyaretçi kalıbını kullanın (bir seçenek değil, yol boyunca daha fazla uygulama olmasını bekliyorum ...), her biri belirli bir uygulamaya uyan yaklaşık 50+ aşırı yükleme ile ?

Soru

Ziyaretçi kalıbını kullanmanızı tavsiye eder misiniz? Bu sorunun çözülmesine yardımcı olabilecek başka bir model var mı?


1
Belki bir dekoratör zinciri daha uygun olurdu?
MattDavey

bazı sorular: bu uygulamalar nasıl farklı? hiyerarşinin yapısı nedir? ve her zaman aynı yapı mıdır? yapıyı her zaman aynı sırada geçmeniz gerekiyor mu?
jk.

@MattDavey: Yani uygulama ve işlem başına bir dekoratör olmasını önerirsiniz?
T. Fabre

2
@ T.Fabre söylemek zor. Orada 50 + uygulamacılarıdır MyInterface.. bu sınıfların her bir ilki gerçekleştiren var mı DoSomethingve DoSomethingElse? Ziyaretçi sınıfınızın aslında hiyerarşiyi facade
nereden geçtiğini görmüyorum

Ayrıca hangi C # sürümü. lambdasın var mı veya linq? emrinde
jk.

Yanıtlar:


13

Ziyaretçi desenini 10 programlama yılı boyunca üç programlama dilinde altı büyük ölçekli projede ifade ağaçlarını temsil etmek için kullanıyorum ve sonuçtan çok memnunum. Desenin uygulanmasını çok daha kolaylaştıran birkaç şey buldum:

Ziyaretçinin arayüzünde aşırı yük kullanmayın

Türü yöntem adına yazın, yani şunu kullanın:

IExpressionVisitor {
    void VisitPrimitive(IPrimitiveExpression expr);
    void VisitComposite(ICompositeExpression expr);
}

ziyade

IExpressionVisitor {
    void Visit(IPrimitiveExpression expr);
    void Visit(ICompositeExpression expr);
}

Ziyaretçi arayüzünüze bir "bilinmeyen yakalama" yöntemi ekleyin.

Kodunuzu değiştiremeyen kullanıcılar bunu mümkün kılar:

IExpressionVisitor {
    void VisitPrimitive(IPrimitiveExpression expr);
    void VisitComposite(ICompositeExpression expr);
    void VisitExpression(IExpression expr);
};

Bu , kendi yakalama yöntemlerini oluşturmalarına IExpressionve IVisitorbu ifadeleri, tümünü yakalama VisitExpressionyöntemlerinin uygulanmasında çalışma zamanı türü bilgilerini kullanarak ifadelerini "anlayabilmelerini" sağlar .

IVisitorArabirimin varsayılan bir "hiçbir şey yapma" uygulaması sağlama

Bu, bir ifade türleri alt kümesiyle uğraşması gereken kullanıcıların ziyaretçilerini daha hızlı oluşturmasına ve kodlarını size daha fazla yöntem ekleyerek bağışık hale getirmesine olanak tanır IVisitor. Örneğin, ifadelerinizden tüm değişken adlarını toplayan bir ziyaretçi yazmak kolay bir iş haline gelir ve IVisitordaha sonra bir sürü yeni ifade türü ekleseniz bile kod kırılmaz .


2
Neden söylediğini açıklayabilir misin Do not use overloads in the interface of the visitor?
Steven Evers

1
Aşırı yüklenmeleri neden önermediğinizi açıklayabilir misiniz? Bir yerde (aslında oodesign.com'da) aşırı yük kullansam da kullanmamam önemli değil. Bu tasarımı tercih etmenizin özel bir nedeni var mı?
T.Fabre

2
@ T.Fabre Hız açısından önemli değil, okunabilirlik açısından önemli. Bunu ( Java ve C #) uyguladığım üç dilden ikisinde yöntem çözünürlüğü , potansiyel aşırı yükler arasında seçim yapmak için bir çalışma zamanı adımı gerektirir ve çok sayıda aşırı yük kodunu okumak biraz zorlaşır. Kodu yeniden düzenlemek de kolaylaşır, çünkü değiştirmek istediğiniz yöntemi seçmek önemsiz bir görev haline gelir.
dasblinkenlight

@SnOrfus Lütfen yukarıdaki T.Fabre'a cevabımı görün.
dasblinkenlight

@dasblinkenlight C # artık çalışma zamanının hangi aşırı yüklenmiş yöntemin kullanılması gerektiğine karar vermesini sağlamak için dinamik (derleme zamanında değil) sunuyor. Aşırı yüklemeyi kullanmamanın hala bir nedeni var mı?
Tintenfiisch
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.