Devo provarlo direttamente o dovrei prima controllare se può essere fatto?

4

Ho una serie di elementi, e ogni articolo nel set deve essere unico. Gli oggetti sono composti da più proprietà e ogni proprietà di ogni oggetto può essere cambiata. Ma dopo ogni cambiamento ogni oggetto nel set deve essere ancora unico.

Dovrei semplicemente provare a cambiare la proprietà:

int err = setOfItems.SetPropertyOfItem( item, property, newValue )
if ( err != 0 ) {
    MessageBox( "You cannot change property to this value" );      
    return;
}
// ( ...or similar exception based code )

O prima dovrei controllare se la proprietà può essere cambiata e solo allora cambiarla:

if ( !setOfItems.CanBePropertyChanged( item, property, newValue ) ) {
    MessageBox( "You cannot change property to this value" );      
    return;
}
setOfItems.SetPropertyOfItem( item, property, newValue );

Quale approccio è migliore e perché?

    
posta user3123061 15.01.2015 - 07:45
fonte

3 risposte

8

Fintanto che il metodo SetPropertyOfItem non non cambia la proprietà quando rileva un conflitto, sceglierei sempre il primo modo di fare le cose. Separare i controlli dalle azioni può funzionare, ma è soggetto a un uso improprio da parte dei chiamanti che eseguono l'azione senza eseguire il controllo. E piegare il controllo nel metodo di azione può portare a sforzi inutili se si dispone anche di un metodo di controllo separato che un chiamante confuso potrebbe chiamare senza necessità.

Un problema con la restituzione dei codici di errore è che il chiamante potrebbe dimenticarsi di controllarlo e ritenere falsamente che si sia verificata una modifica quando non è stata eseguita. Se questo è probabile che sia un problema, l'aumento delle eccezioni invece dei codici di errore è un'opzione.

(Ci sono anche persone che sostengono che dovresti mai restituire nulla da un metodo con effetti collaterali. Ritengo che l'estremismo miope, come affermare che lo stile funzionale puro al 100% sia il giusto scelta per tutto. Penso che le decisioni di progettazione debbano sempre essere prese considerando le pressioni e il contesto nella situazione attuale ).

    
risposta data 15.01.2015 - 08:36
fonte
1

Dipende dal dominio e dai requisiti dell'interfaccia utente. Un sito / programma dovrebbe avvisare l'utente di possibili problemi? Se è così, hai bisogno di un metodo per controllare.

bool CanBeChanged(item, property, newValue);

Un solito setter deve essere normale. È vuoto, cioè solleva un'eccezione:

void Change(item, property, newValue);

Che cosa succede se un utente non desidera un'eccezione, vuole il codice di ritorno:

bool TryChange(item, property, newValue);

La parola "Prova" indica a un utente di classe che il metodo non genera eccezioni.

Poiché un controllo dell'unicità può essere un'operazione lunga, TryChange può causare errori nell'applicazione di multithreading. Un utente potrebbe essere in dubbio: il tuo metodo è thread-safe?

bool TryChangeConcurrently(item, property, newValue);

In generale è una cattiva pratica divulgare i dettagli dell'implementazione nei nomi delle classi e dei metodi del dominio, quindi è ancora preferibile TryChange . Considera, le classi di dominio devono essere thread-safe il più possibile.

Naturalmente, tutto questo è un aspetto generale.

In C ++ can_be_changed deve essere dichiarato come const . La dichiarazione di change in Java dovrebbe contenere la clausola throws , mentre canBeChanged e tryChange non dovrebbero. In C # qualsiasi metodo setter ha una forma set[item, property] = value . Ogni lingua offre i propri idiomi e possiamo seguirli al meglio.

Riepilogo: lascerei tutti e tre i metodi e proverei a rendere la classe sicura per i thread.

    
risposta data 15.01.2015 - 14:46
fonte
1

(disclaimer: uso della conoscenza di Java di seguito)

Nella tua seconda soluzione, presumo che uno dei due approcci si svolgerà per CanBePropertyChanged :

  1. Passa in rassegna tutte le altre voci del tuo Set per assicurarti che nessuno contenga oldProperty1 , oldProperty2 ... newPropertyN .
  2. Costruisci un oggetto temporaneo temp con oldProperty1 , oldProperty2 ... newPropertyN e utilizza un metodo standard, ad es. Set.contains(temp) , controlla che il metodo restituisca false .

Se non ci sono corrispondenze, il tuo SetPropertyOfItem applicherà la nuova proprietà senza ulteriori verifiche.

Nella tua prima soluzione, presumo stia facendo qualcosa di simile agli approcci per CanBePropertyChanged , solo che questo è fatto come primo passo in SetPropertyOfItem . Se Item non può essere modificato, viene restituito un "codice errore" diverso da zero.

Il problema che vedo con (1) sopra è che duplicherete il controllo di equivalenza della vostra classe, che potrebbe non essere a prova di futuro se il vostro Items acquisisce nuove proprietà o vincoli che influenzano il modo in cui viene eseguita questa equivalenza. Per entrambe le soluzioni, la concorrenza, se dovessi scegliere di implementarla, introduce la piccola possibilità che la modifica effettiva dell'oggetto Item non sia sincronizzata con altre operazioni di lettura / scrittura simultanee sull'oggetto Set o Item .

alternative?

Pertanto, mi avvicinerò a questa domanda ponendo questa domanda : Quanto è costoso ricostruire un nuovo updatedItem con oldProperty1 , oldProperty2 ... newPropertyN ? Se si tratta di un'operazione relativamente economica (ad esempio, non è necessario inviare queste proprietà a un database per recuperare dati che necessitano un'ulteriore analisi), perché non prendere in considerazione la creazione di Item una classe immutabile? essere simile all'approccio (2), solo che aggiungerai il nuovo updatedItem non permanente (permanente alla prossima modifica) nel tuo Set e rimuovendo il vecchio originalItem da esso. Se aiuta, puoi anche introdurre un po 'di zucchero sintattico:

Item updatedItem = oldItem.createWithUpdate( property, newValue ); // creates a new object
if ( set.add( updatedItem ) ) {
    set.remove(oldItem);
} else {
    log.warn( "You cannot change property to this value" );
}

Il tuo codice può essere più semplice di conseguenza. Inoltre, un Set per definizione non contiene valori duplicati, quindi l'istruzione

But after each change every item in the set must still be unique

È praticamente un dato dopo un'operazione add() (che, in Java, restituisce true se Set did non contiene già l'elemento specificato ). Inoltre, quando la tua classe è immutabile, c'è meno motivo di preoccuparsi di introdurre la concorrenza, dal momento che gli oggetti stessi non sarebbero mutati fuori sincrono. Dovrai solo codificare in modo difensivo contro le operazioni simultanee sul tuo oggetto Set .

Per rispondere direttamente alla domanda

Ok, se Item è in effetti già immutabile, o preferisci non apportare le modifiche proposte sopra, e devo scegliere tra due approcci, andrò con la prima soluzione. Tuttavia, anziché un codice di ritorno basato su int , restituirò un boolean o utilizzerò la gestione delle eccezioni. Favorirò la prima soluzione perché apre la possibilità in futuro di modificare l'implementazione di SetPropertyOfItem , in senso lato. La risposta di @Kilian Foth dice che è meglio in questo senso.

    
risposta data 16.01.2015 - 09:04
fonte

Leggi altre domande sui tag