Sto cercando di comprendere la struttura della classe per il linguaggio specifico del dominio

4

Il mio lavoro è principalmente nella programmazione di sistemi embedded in C, e l'adeguata struttura di classe per farcela mi sfugge. Attualmente comunichiamo tramite C # e Visual Basic con una vasta collezione di servi, pompe e sensori tramite un dispositivo nascosto USB-to-CAN.

Al momento, è piuttosto complicato comunicare con i dispositivi. Per leggere la versione del firmware del controller numero 1, utilizzare:

SendCan(Controller,1,ReadFirmwareVersion) or
SendCan(8,1,71)

Questo invia tre byte sul CAN bus: (8,1,71)

Collegati ai controller sono vari sensori.

SendCan(Controller,1,PassThroughCommand,O2Sensor,2,ReadO2)

direbbe al Controller numero 1 di passare un comando a O2 Sensor number 2 per leggere O2 inviando i byte 8,1.200,16,2,0

Mi piacerebbe sviluppare un linguaggio specifico del dominio per questa configurazione. Invece di comandi emessi come sono attualmente, i comandi dovrebbero essere scritti in questo modo:

Controller1.SendCommand.O2Sensor2.ReadO2

per inviare i byte 8,1,200,16,0

Qual è il modo migliore per farlo? Alcune macchine hanno 20 sensori O2, altri hanno 5 controller, quindi i numeri e i tipi di controller e sensori, pompe, ecc. Non sono statici.

    
posta drinck 06.04.2012 - 18:26
fonte

5 risposte

2

Preferirei scrivere

machine1.Controllers[1].O2Sensors[2].ReadO2();

Non è necessario avere un SendCommand in là.

Aggiorna

Ecco il mio suggerimento per una classe controller

public class Controller
{
    private const int ReadFirmwareVersionCmd = 71;
    private const int PassThroughCommandCmd = 200;

    private int _controllerNumber;

    public Controller(int controllerNumber)
    {
        _controllerNumber = controllerNumber;
        O2Sensors = new List<O2Sensor>();
    }

    public List<O2Sensor> O2Sensors { get; private set; }

    private void SendControllerCommand(int controllerCommand)
    {
        Controller.SendCan(8, _controllerNumber, controllerCommand);
    }

    public void SendSensorCommand(int sensorType, int sensorNo, int sensorCommand)
    {
        Controller.SendCan(8, _controllerNumber, PassThroughCommandCmd, sensorType, sensorNo, sensorCommand);
    }

    private static void SendCan(int deviceType, int deviceNo, int commandNo)
    {
        // ...
    }

    private static void SendCan(int deviceType, int deviceNo, int commandNo,
                                int sensorType, int sensorNo, int sensorCommand)
    {
        // ...
    }

    public void ReadFirmwareVersion( )
    {
        SendControllerCommand(ReadFirmwareVersionCmd);
    }
}

Vorrei ricavare tutti i tipi di sensori da una base comune

public abstract class SensorBase
{
    protected Controller _controller;
    protected int _sensorNo;

    public SensorBase(Controller controller, int sensorNo)
    {
        _controller = controller;
        _sensorNo = sensorNo;
    }

    public abstract void Read();
}

Un sensore O2 come esempio per un sensore

public class O2Sensor : SensorBase
{
    public O2Sensor(Controller controller, int sensorNo)
        : base(controller, sensorNo)
    {
    }

    public override void Read()
    {
        _controller.SendSensorCommand(16, _sensorNo, 0);
    }
}

Puoi inizializzare un controller come questo

var controller1 = new Controller(1);
controller1.O2Sensors.Add(new O2Sensor(controller1, 1));
controller1.O2Sensors.Add(new O2Sensor(controller1, 2));

Ora puoi leggere informazioni come questa

controller1.ReadFirmwareVersion();
controller1.O2Sensors[1].Read();
    
risposta data 06.04.2012 - 18:58
fonte
5

Il tuo pensiero è ancora troppo basso. Stai cercando un modo migliore per dire cose come "Qual è il valore di O2 rilevato da O2 Sensor # 2?" Ma perché hai bisogno di sapere quel valore? Che cosa hai intenzione di fare in base al valore?

Se vuoi solo un involucro un po 'meno ingombrante, certo, scrivilo. Ma se vuoi un vero linguaggio specifico per il dominio, devi iniziare a pensare a un livello molto più alto. Devi anche prendere in considerazione la creazione di oggetti che sono più capaci.

Supponiamo che tu debba mostrare un avviso se il livello di O2 è troppo basso. Semplice:

if (new Controller().O2Sensors[1].Value < 30)
    MessageBox.Show("The canary has died.");

Semplice, ma non buono. Per ottenere la progettazione OO più flessibile, devi dire agli oggetti cosa fare piuttosto che eseguirne una query :

new Controller().ShowAlertIfMainOxygenLevelTooLow();

Quindi il Controller stesso sa quale sensore di ossigeno è il principale e sa cosa significa "troppo basso".

Ma cosa succede se "troppo basso" differisce dall'ambiente? Forse il tuo controller potrebbe essere usato su una stazione spaziale, e l'ambiente O2 è più ricco, e anche una piccola goccia indica problemi. In tal caso, dovresti costruire il tuo Controller con una strategia appropriata, ad esempio:

public class Controller
{
    public Controller(IOxygenLevelDetector oxygenLevelDetector)
    {
        [...]
    }
}

L'interfaccia IOxygenLevelDetector potrebbe avere un metodo che accetta il sensore O2, legge il livello O2 corrente e restituisce un bool che indica se il livello è troppo basso o se il livello è in uno stato di successo o di errore. ..qualunque abbia più senso per te. Costruisci il tuo Controller con l'ambiente appropriato:

new Controller(new SpaceStation())

o

new Controller(new RoomOnEarth())

Naturalmente, ad un certo punto stai ancora leggendo il livello di ossigeno e poi facendo qualcosa. Come fai a sapere quando è il posto sbagliato per farlo? Una buona regola è quella di minimizzare i punti . Qualcosa di simile

new ServiceBoard().Controllers[5].O2Sensors[1].OxygenGauge.Value

è troppi punti. ServiceBoard ha troppa visibilità all'interno di molti altri oggetti. Tutto quello che dovrebbe fare è andare su un livello: dire a Controller di fare qualcosa, e aspettare la risposta. Il Controller , a sua volta, dovrebbe dire a O2Sensor di fare qualcosa, e dovrebbe aspettare la risposta. In questo modo, anche quando devi apportare modifiche, eviti di farle ondeggiare lungo l'intero programma.

    
risposta data 06.04.2012 - 19:50
fonte
3

Perché non fare qualcosa di simile:

Controller.At(1).SendCommand().O2Sensor.At(2).ReadO2();

In questo modo puoi ospitare tutti i controller, tutti i sensori O2 ed è ancora abbastanza chiaro.

    
risposta data 06.04.2012 - 18:37
fonte
3

Questo è ancora troppo prolisso per i miei gusti, a causa della mancanza di incapsulamento. Non so molto sulla tua topologia o sulla sovrapposizione di comandi tra sensori, ma la mia prima scelta per un'API per i tuoi esempi sarebbe semplicemente:

ReadFirmwareVersion(1);
ReadO2(1, 2);

Se avessi bisogno che fosse più orientato agli oggetti e non dovessi passare attorno al controller e agli indirizzi dei dispositivi per tutto il tempo, lo farei apparire in questo modo:

controller = new Controller(1);
controller.readFirmwareVersion();
o2Sensor = new O2Sensor(controller, 2);
o2Sensor.readO2();

Non c'è bisogno di una DSL qui. Dovresti essere in grado di semplificare l'API entro i confini della tua lingua esistente.

    
risposta data 06.04.2012 - 19:01
fonte
1

La maggior parte della gente implementa interfacce in stile fluente è prima di implementare una sorta di interfaccia di oggetti standard, quindi avvolgere le cose in interfacce con i metodi di estensione per arrivare alla fluente.

    
risposta data 06.04.2012 - 21:33
fonte

Leggi altre domande sui tag