Programmazione orientata agli oggetti: getter / setter o nomi logici

12

Attualmente sto pensando a un'interfaccia per una classe che sto scrivendo. Questa classe contiene stili per un personaggio, ad esempio se il carattere è grassetto, corsivo, sottolineato, ecc. Ho discusso con me stesso per due giorni se dovrei usare getter / setter o nomi logici per i metodi che cambiano i valori in questi stili. Mentre io preferisco i nomi logici, vuol dire scrivere codice che non è altrettanto efficiente e non logico. Lascia che ti faccia un esempio.

Ho una classe CharacterStyles che ha variabili membro bold , italic , underline (e alcune altre, ma le lascerò fuori per mantenerla semplice). Il modo più semplice per consentire ad altre parti del programma di accedere a queste variabili, è scrivere metodi getter / setter, in modo che tu possa fare styles.setBold(true) e styles.setItalic(false) .

Ma non mi piace. Non solo perché molte persone dicono che i getter / setter rompono l'incapsulamento (è davvero così brutto?), Ma soprattutto perché non mi sembra logico. Mi aspetto di disegnare un personaggio attraverso un metodo, styles.format("bold", true) o qualcosa del genere, ma non attraverso tutti questi metodi.

C'è un problema però. Dal momento che non puoi accedere a una variabile membro dell'oggetto dal contenuto di una stringa in C ++, dovrei scrivere un grande contenitore if-statement / switch per tutti gli stili, o dovrei memorizzare gli stili in una matrice associativa ( mappa).

Non riesco a capire quale sia il modo migliore. Un momento penso che dovrei scrivere i getter / setter, e il momento dopo mi chino verso l'altro modo. La mia domanda è: cosa faresti? E perché lo faresti?

    
posta Frog 24.08.2012 - 23:23
fonte

4 risposte

6

Sì, i getter / setter interrompono l'incapsulamento - sono fondamentalmente solo uno strato in più tra l'accesso diretto al campo sottostante. Potresti anche solo accedervi direttamente.

Ora, se vuoi che i metodi più complessi accedano al campo, è valido, ma invece di esporre il campo, devi pensare a quali metodi dovrebbe essere la classe invece di offrire. vale a dire. invece di una classe Bank con un valore Money esposto utilizzando una proprietà, è necessario pensare a quali tipi di accesso un oggetto Bank dovrebbe offrire (aggiungere denaro, prelevare, ottenere un saldo) e implementare quelli invece. Una proprietà sulla variabile Money è solo sintatticamente diversa dall'esporre direttamente la variabile Money.

DrDobbs ha un articolo che dice di più.

Per il tuo problema, avrei 2 metodi setStyle e clearStyle (o qualsiasi altra cosa) che prendono un enum dei possibili stili. Un'istruzione switch all'interno di questi metodi applicherebbe quindi i valori rilevanti alle variabili di classe appropriate. In questo modo, puoi cambiare la rappresentazione interna degli stili in qualcos'altro se in seguito decidi di memorizzarli come una stringa (per esempio in HTML) - qualcosa che richiederebbe a richiedere la modifica di tutti gli utenti della classe anche se hai utilizzato le proprietà get / set.

È ancora possibile attivare stringhe se si desidera prendere valori arbitrari, avere una dichiarazione if-then di grandi dimensioni (se ce ne sono alcune) o una mappa di valori stringa per i puntatori del metodo utilizzando std :: mem_fun (o std :: function ) così" bold "verrebbe memorizzato in una chiave map con il suo valore sts :: mem_fun in un metodo che imposta la variabile bold su true (se le stringhe sono lo stesso dei nomi delle variabili membro, quindi puoi anche utilizzare la stringa di stringhe per ridurre la quantità di codice devi scrivere)

    
risposta data 25.08.2012 - 00:44
fonte
11

Un'idea che potresti non aver preso in considerazione è il pattern Decorator . Piuttosto che impostare i flag in un oggetto e quindi applicare tali flag a qualsiasi cosa tu stia scrivendo, puoi avvolgere la classe che fa la scrittura in Decorators, che a sua volta applica gli stili.

Il codice chiamante non ha bisogno di sapere quanti di questi wrapper hai messo intorno al tuo testo, basta chiamare un metodo sull'oggetto esterno e richiama lo stack.

Per un esempio pseudocodice:

class TextWriter : TextDrawingInterface {
    public:
        void WriteString(string x) {
            // write some text somewhere somehow
        }
}

class BoldDecorator : TextDrawingInterface {
    public:
        void WriteString(string x) {
            // bold application on
            m_textWriter.WriteString(x);
            // bold application off
        }

        ctor (TextDrawingInterface textWriter) {
            m_textWriter = textWriter;
        }

    private:
        TextWriter m_TextWriter;
}

E così via, per ogni stile di decorazione. Nel suo utilizzo più semplice, puoi quindi dire

TextDrawingInterface GetDecoratedTextWriter() {
    return new BoldDecorator(new ItalicDecorator(new TextWriter()));
}

E il codice che chiama questo metodo non ha bisogno di conoscere i dettagli di ciò che sta ricevendo. Finché è QUALCOSA che può disegnare il testo attraverso un metodo WriteString.

    
risposta data 24.08.2012 - 23:54
fonte
0

Mi piacerebbe la seconda soluzione. Sembra più elegante e flessibile. Sebbene sia difficile immaginare altri tipi oltre che grassetto, corsivo e sottolineato (overline?), Sarebbe difficile aggiungere nuovi tipi usando le variabili membro.

Ho usato questo approccio in uno dei miei ultimi progetti. Ho una classe che può avere più attributi booleani. Il numero e i nomi degli attributi possono cambiare nel tempo. Li memorizzo in un dizionario. Se un attributo non è presente, suppongo che il suo valore sia "falso". Devo anche memorizzare un elenco di nomi di attributi disponibili, ma questa è un'altra storia.

    
risposta data 24.08.2012 - 23:55
fonte
0

Penso che tu stia pensando troppo al problema.

styles.setBold(true) e styles.setItalic(false) sono OK

    
risposta data 25.08.2012 - 04:23
fonte

Leggi altre domande sui tag