C # Come evitare codice duplicato con interfacce in cui gli oggetti hanno lo stesso comportamento

1

Quindi un semplice esempio che ho:

public interface IFollow{
    Transform Target {get;}
    void LateUpdate();
}

public A : Monobehaviour , IFollow {

   public Transform Target {get; set;}
   public void LateUpdate(){
      //follow the target
   }
}

public B : Monobehaviour , IFollow {

   public Transform Target {get; set;}
   public void LateUpdate(){
      //follow the target [duplicate code as seen in A]
}

Quando hai molti oggetti diversi che seguono un bersaglio con la stessa logica, l'interfaccia implementata ti costringe a scrivere un sacco di codice duplicato.

Inoltre non ha molto senso farlo tramite una classe base poiché non tutti gli oggetti seguiranno.

Che cos'è un modo più semplice per farlo senza troppe duplicazioni?

Encase any wondering, Monobehaviour è una cosa di Unity3D, anche se non eccessivamente rilevante per la domanda, ma ho pensato di parlarne.

    
posta WDUK 10.07.2018 - 03:13
fonte

3 risposte

4

Hai due opzioni:

  • Implementa in una (n) (astratta) classe base
  • Implementare un adattatore helper

Crea una classe base o una classe base astratta che implementa il comportamento comune ed eredita da essa. In questo modo il tuo codice vive in un posto e non è duplicato.

Se una classe base non funziona per qualche motivo, è possibile inserire il codice in un oggetto adattatore (o strategia) che ogni classe utilizza per implementare il comportamento, spostando nuovamente il codice duplicato in un unico punto.

    
risposta data 10.07.2018 - 03:30
fonte
2

Puoi prima estrarre il / i comportamento / i comune / i in classi semplici che implementano solo IFollow :

public sealed class FollowTarget : IFollow
{
    public Transform Target {get; set;}
    public void LateUpdate() { //follow the target }
}

public sealed class FollowSomethingElse : IFollow
{
    public Transform Target {get; set;}
    public void LateUpdate() { //follow something else }
}

Quindi utilizza l'implementazione richiesta in A , B , C :

public sealed A : Monobehaviour , IFollow {
    private readonly IFollow _follow;
    public A() => _follow = new FollowTarget();
    public Transform Target {get => _follow.Target; set => _follow.Target = value; }
    public void LateUpdate() => _follow.LateUpdate();
}

public sealed B : Monobehaviour , IFollow {
    private readonly IFollow _follow;
    public B() => _follow = new FollowTarget();
    public Transform Target {get => _follow.Target; set => _follow.Target = value; }
    public void LateUpdate() => _follow.LateUpdate();
}

public sealed C : Monobehaviour , IFollow {
    private readonly IFollow _follow;
    public C() => _follow = new FollowSomethingElse();
    public Transform Target {get => _follow.Target; set => _follow.Target = value; }
    public void LateUpdate() => _follow.LateUpdate();
}

Quindi a questo punto potresti vedere che A / C o B / C condividono la stessa implementazione eccetto che non instanziate lo stesso _follow . In questo caso puoi richiedere un IFollow nel costruttore e refactor (semplificare) il tuo codice in questo modo:

public sealed AorC : Monobehaviour , IFollow {
    private readonly IFollow _follow;
    public AorC(IFollow follow) => _follow = follow;
    public Transform Target {get => _follow.Target; set => _follow.Target = value; }
    public void LateUpdate() => _follow.LateUpdate();
}

public sealed B : Monobehaviour , IFollow {
    private readonly IFollow _follow;
    public B() => _follow = new FollowTarget();
    public Transform Target {get => _follow.Target; set => _follow.Target = value; }
    public void LateUpdate() => _follow.LateUpdate();
}

Utilizzo in corso

var a = new AorC(new FollowTarget()); 
var c = new AorC(new FollowSomethingElse());

Questo approccio è solitamente denominato Decorator ed è una forma specializzata di composizione che promuove un codice più componibile.

    
risposta data 10.07.2018 - 14:48
fonte
1

È possibile rimuovere il metodo (ma non l'interfaccia) dalla classe e spostare la logica in un metodo di estensione:

public interface IFollow
{
    Transform Target {get;}
}

static public class ExtensionMethods
{
     static public void LateUpdate<T>(this T source) where T : IFollow
     {
         var target = source.Target; //Works, due to the type constraint
         //Add code to follow the target here
     }
}

public A : Monobehaviour , IFollow
{
    public Transform Target {get; set;}
}

public B : Monobehaviour , IFollow
{
    public Transform Target {get; set;}
}

Lo chiameresti ancora nello stesso modo di prima, solo devi ricordare di includere lo spazio dei nomi del tuo metodo di estensione (se è diverso):

A a = new A();
a.LateUpdate();

Potresti anche combinare questi metodi; ad esempio, potresti lasciare LateUpdate() nell'interfaccia, ma scrivere l'implementazione come pass-through che chiama solo il metodo di estensione dietro le quinte. Questo sarebbe utile se hai bisogno del metodo nell'interfaccia per qualche motivo.

    
risposta data 10.07.2018 - 04:42
fonte

Leggi altre domande sui tag