Opzioni per le classi derivate di due classi base astratte

1

Diciamo che ci sono classi D1, D2, ecc. che descrivono diversi tipi di una classe astratta D.

Diciamo che ci sono classi SenderReceiver che descrivono diversi modi di comunicare per ogni D1, D2, ecc.:

abstract class SenderReceiver {
    abstract void Setup();
    abstract void SendA();
    public void M1() { /* Common code */ }
    ...
    public void MN() { /* Common code */ }
}

abstract class SenderReceiverPlus : SenderReceiver {
    abstract void SendB();
}

class SenderReceiverPlusTcp : SenderReceiverPlus {
    override void Setup() { /* BeginConnect code */ }
}

class SenderReceiverTcp : SenderReceiver {
    override void Setup() { /* Same code as above (vomit) */ }
}

class SenderReceiverPlusTcpD1 : SenderReceiverPlusTcp {
    override void SendA()  { /* Code */ }
    override void SendB()  { /* Code */ }
}

class SenderReceiverTcpD2 : SenderReceiverTcp {
    override void SendA()  { /* Code */ }
}

class SenderReceiverPlusUdp : SenderReceiverPlus {
    override void Setup() { /* Socket bind code */ }
}

class SenderReceiverUdp : SenderReceiver {
    override void Setup() { /* Same code as above (vomit) */ }
}

class SenderReceiverPlusUdpD3 : SenderReceiverPlusUdp {
    override void SendA()  { /* Code */ }
    override void SendB()  { /* Code */ }
}

class SenderReceiverUdpD4 : SenderReceiverUdp {
    override void SendA()  { /* Code */ }
}

abstract class D {
    SenderReceiver S;
    abstract void DoThing();
}

class D1 : D {
    D1 () { S = new SenderReceiverPlusTcpD1(); }
    override void DoThing() { S.SendA(); S.SendB(); S.M1(); }
}

class D2 : D {
    D2 () { S = new SenderReceiverTcpD2(); }
    override void DoThing() { S.SendA(); S.M1(); }
}

class D3 : D {
    D1 () { S = new SenderReceiverPlusUdpD3(); }
    override void DoThing() { S.SendA(); S.SendB(); S.M1(); }
}

class D4 : D {
    D2 () { S = new SenderReceiverUdpD4(); }
    override void DoThing() { S.SendA(); S.M1(); }
}

E c'è una classe con un elenco di oggetti D:

class Controller {
    List<D> ds;

    public Controller() {
        ds = new List<D>(new [] {new D1(), new D2(), new D3(), new D4()});
    }

    public DoAllThings() {
        foreach (D d in ds) {
            d.DoThing();
        }
    }
}

Questo non funziona perché (per uno) il riferimento di D1 a S fa riferimento a SenderReceiver e quindi SendB () non è sicuramente presente. Inoltre, è lordo e c'è del codice duplicato nei diversi tipi di Tcp e Udp.

Ho considerato metodi virtuali vuoti nella classe base, lanciando S al tipo corretto in D1 e D2 e non avendo S nella classe D. Nessuno di questi è particolarmente soddisfacente.

C'è un modo migliore per farlo?

EDIT:

Codice aggiornato per mostrare i tentativi nel codice corrente con flessibilità per i protocolli Udp / Tcp.

EDIT2: Quindi è chiaro che la gerarchia delle classi qui è un po 'esagerata. Per semplificarlo, potrei fare

abstract class SenderReceiver {
    Socket _Socket;
    protected class StateObject { /* For async network methods */ }
    void Dispose();
    void Exit();
}
class SenderReceiverTcp : SenderReceiver {
    void Connect() { /* Sets up socket and calls BeginConnect */ }
    void EndConnect() { /* Callback for Connect() */ }

    void Accept() { /* Sets up socket, Bind, Listen, BeginAccept */ }
    void EndAccept() { /* Callback for Accept() */ }

    void Send(msg) { /* Send message */ }
    void Receive() { /* Receive or BeginReceive */ }
    void EndReceive() { /* Callback if Receive is async */ }
}

class SenderReceiverUdp : SenderReceiver {
    void Connect() { /* Sets up socket and calls Bind */ }

    void Send(msg) { /* Send message */ }
    void Receive() { /* Receive or BeginReceive */ }
    void EndReceive() { /* Callback if Receive is async */ }
}

class SenderRecevierD1 {
    SenderReceiverTcp _SenderReceiverTcp;

    void Connect() { _SenderReceiverTcp.Connect(); }
    void Accept() { _SenderReceiverTcp.Accept(); }
    void Send() { msg.Create(); _SenderReceiverTcp.Send(msg); }
    ...
}

class SenderReceiverD2 {
    SenderReceiverUdp _SenderReceiverUdp;

    void Connect() { _SenderReceiverUdp.Connect(); }
    void Send() { msg.Create(); _SenderReceiverUdp.Send(msg);
                _SenderReceiverUdp.Receive();
                if (!_SenderReceiverUdp.ReceiveEnded()) // Check for ack
                    _SenderReceiverUdp.Send(msg);
                }
    ...
}

In questo modo, le classi D # non hanno bisogno di sapere come inviare messaggi per il loro tipo (forse una classe D con tentativi TCP se non c'è riconoscimento mentre un'altra non aspetta un riconoscimento perché quel tipo D non lo fa t send acknowgments) ma le differenze nell'impostazione e nell'invio di TCP / UDP sono astratte nelle classi SenderReceiverTcp / Udp.

Questa è una sorta di estensione di "Rimozione di SenderReceiver dalla classe base". Non voglio postare questa risposta come risposta perché non sono positivo se è al massimo. Lo lascerò per un po 'e attendo il feedback della community.

    
posta Mark Lodato 24.04.2017 - 15:52
fonte

2 risposte

1

Ho cercato di capire la tua domanda ed esempi, e credo che il pattern Command potrebbe aiutare a migliorare il tuo design. Di seguito è riportato un codice che spiega cosa ho pensato:

abstract class Communicator {
    abstract void Setup();
    public void M1() { /* Common code */ }
    ...
    public void MN() { /* Common code */ }
}

class TcpCommunicator {
    override void Setup() { //specific code }
}
class UcpCommunicator {
    override void Setup() { //specific code }
}

//...and there might be other communicator implementations...

interface ISendCommand {
    Communicator communicator;
    void execute();
}

class SendACommand : ISendCommand {
    Communicator communicator; //injected via constructor
    void execute();
}
class SendBCommand : ISendCommand {
    Communicator communicator; //injected via constructor
    void execute();
}

//...and there might be other types of messages to be sent...


abstract class D {
    Communicator S;
    abstract void DoThing();
}

class D1 : D {
    D1 () { S = new TcpCommunicator(); }
    override void DoThing() { 
        new SendACommand(S).execute(); 
        new SendBCommand(S).execute(); 
        S.M1(); 
    }
}

class D2 : D {
    D2 () { S = new TcpCommunicator(); }
    override void DoThing() {
        new SendACommand(S).execute();  
        S.M1(); 
    }
}

class D3 : D {
    D1 () { S = new UdpCommunicator(); }
    override void DoThing() { 
        new SendACommand(S).execute(); 
        new SendBCommand(S).execute(); 
        S.M1(); 
    }
}

class D4 : D {
    D2 () { S = new UdpCommunicator(); }
    override void DoThing() { 
        new SendACommand(S).execute();  
        S.M1(); 
    }
}

//...and so on...
    
risposta data 23.10.2017 - 19:50
fonte
0

Una possibilità che potrebbe funzionare è creare due nuove interfacce

public interface ASenderReceiver {
    void SendA();
    void Close();
}

public interface BSenderReceiver {
    void SendB();
    void Close();
}

Quindi crea una classe composita

public class ABSenderReceiver : ASenderReceiver, BSenderReceiver {
    private readonly ASenderReceiver A;
    private readonly BSenderReceiver B;

    public void SendA() {
        A.SendA();
   }

    public void SendB() {
        B.SendB();
   } 

    public void A.Close() {
        A.Close();
   } 

    public void B.Close() {
        B.Close();
   } 
} 

Poi passa la nuova classe nel tuo codice.

    
risposta data 24.04.2017 - 17:08
fonte

Leggi altre domande sui tag