2010-02-08 7 views
12

O in parole più specifiche, è "ok" non fare affidamento su setter e getter?È ragionevole avere una discreta quantità di proprietà pubbliche in una classe?

Ho a che fare con una classe che controlla la disponibilità di stanze e imposta proprietà pubbliche di cui ce ne sono più di una dozzina. Cose come:

  • unitNumber
  • tipi di sistemazione (array)
  • codeCorporate
  • CodeGroup
  • numberKids
  • numberAdults
  • numberRooms
  • valuta
  • minrate
  • maxrate
  • SoapServer
  • unità (array)
  • hotelId

E dopo che un oggetto è un'istanza queste proprietà sono impostate con $this-> all'interno vari metodi. Tuttavia il codice che si occupa con l'oggetto imposta spesso proprietà pubbliche direttamente invece di usare metodi getter/setter:

$object->something = 3; 

foreach ($object->things as $thing) { } 

Se ho il tempo di refactoring questa classe ..

  • Dovrei attaccare tutti queste proprietà in un array di dati che è una proprietà privata e definiscono i metodi __set e __get?
  • Devo fare un singolo metodo getter per ciascuna proprietà?

risposta

3

A mio parere, raramente è una buona idea avere membri pubblici. Aumenta l'accoppiamento tra le classi e rende il refactoring molto complicato (se ne hai bisogno.)

Setter/Getter sono la strada da percorrere, e la penalità di prestazioni molto piccola che si paga per questo è solitamente ottimizzata o trumped dall'eleganza.

Per rispondere alla domanda su array e single-getter-per-var, è una questione di gusti. Tendo a mantenere solo le variabili di un tipo simile all'interno di un array e separo il resto.

+0

Quindi, come definiresti personalmente il metodo getter? Usereste '__get'? –

3

personalmente ho ancora trovato un vero buon motivo per una proprietà pubblica, anche se im per suggerimento aperta :-)

anche se io preferisco di gran lunga getter/setter specificati per ogni proprietà (che sia un proxy per generalizzato get($name) oppure no). Presumo che tu abbia già un altro codice che usa l'assegnazione diretta, quindi in tal caso direi di continuare ad usare i metodi magici __get/__set.

2

Penso che la maggior parte delle persone raccomanderà l'uso dei setter & getter. In questo momento sei limitato semplicemente a impostare & che preleva la proprietà, ma cosa succede se vuoi accedere quando quella proprietà è accessibile?O forse vuoi eseguire il valore con una funzione di convalida prima (email, numero di telefono, codice postale, ecc.). Forse dovrai chiamare un'altra funzione o impostare un'altra proprietà. Penso che tu veda dove sto andando con questo. Usando setter & getter, aggiungi un prezioso livello di incapsulamento alle tue classi, e il 99% delle volte vale la digitazione in più che dovrai fare;) Immagina di provare a fare gli esempi sopra senza setter & getter. Sarebbe un grosso mal di testa per non dire altro.

Modifica: Ho dimenticato di menzionare Doctrine. È un Object Mapper Mapper (ORM) che può automaticamente impostare setter & getter per te (tra le altre cose). È possibile controllare fuori a http://www.doctrine-project.org/

+1

In realtà l'ultima volta che ho controllato Doctrine (1.2) non è stato in grado di generare setter/getter per te a causa dell'incapacità di determinare correttamente determinati tipi di possibilità di relazione. Il meglio che poteva fare era impostare i metodi magici per te. Detto questo, in qualche modo il plugin Doctrine di Symfony aggiunge in modo affidabile quei getter/setter, ma presumo ci sia qualcos'altro che hanno aggiunto al proprio plugin per rendere valide le assunzioni necessarie per questo (forse eliminando alcuni tipi di funzionalità alias). – prodigitalson

+0

Grazie per le informazioni, prodigitalon. Mi capita di usare Symfony + Doctrine, quindi questo spiega perché non ho incontrato questo problema. Spero che il pacchetto autonomo di Doctrine lo supporti nuovamente in futuro. –

1

Vorrei fare un passo indietro e fare alcune domande più generali:

  • Perché ho a esporre questo molte informazioni; che cosa lo sta usando e perché?
  • Questa classe è davvero solo una struttura di dati senza comportamento, nel qual caso dovrebbe essere una classe privata per qualche altra classe?
  • Questa classe ha un unico scopo o è sul percorso per diventare monolitica?

si può scoprire che siete in grado di creare viste di un'istanza di una classe per esportare in un database, visualizzazione in forma, ecc Scopri il "costruttore" e modelli "aciclico di promozione" per iniziare .

Per quanto riguarda gli accessor, non vedo la necessità di usarli per ciò che si sta descrivendo: recuperare le proprietà di classe e le informazioni di stato interne, ovvero una struttura. Tuttavia, per gli attributi di una classe ho potuto vedere il vantaggio in alcuni casi, ma più per il recupero degli attributi, non per le mutazioni dello stato dell'oggetto.

1

Se posso aggiungere il mio granello di sale alcuni mesi dopo:

E 'molto non-OO per avere proprietà pubbliche. Tutto dovrebbe essere incapsulato, semplicemente perché (tra le altre ragioni) l'uso della manipolazione diretta degli attributi non fornisce metodi per eseguire facilmente il refactoring o eseguire (più) controlli di controllo quando alcune fonti esterne modificano il campo. Ad esempio, supponiamo di avere una classe con molti campi che vengono utilizzati più volte in un progetto e che il progetto contiene diverse migliaia di file; è un progetto che è in corso e ampliato da alcuni anni. Diciamo che la società sta cambiando il suo modello di business, o che si riscontra un problema con alcuni tipi di dati del campo e ora è necessario avere qualche convalida; duplicherete quella convalida in tutte quelle migliaia di codice sorgente che accede direttamente al membro pubblico? In PHP, la soluzione potrebbe essere semplice, ma non nella maggior parte del linguaggio di programmazione OO (ad esempio Java). Il fatto è che OO è based on encapsulation. In breve, l'incapsulamento non produce solo codice pulito, ma anche codice altamente manutenibile (per non dire economico e coeso).

Il tuo suggerimento di avere un membro privato (array) manipolato da __get/__set è buono. In questo modo, se hai bisogno di una validazione extra lungo la strada, crea semplicemente il tuo setter e/o il tuo getter e sarà la fine di esso. Qualcuno potrebbe obiettare che è controproducente dato che il completamento del codice non può dare il via a __get/__set. IMHO, basandosi sul completamento del codice è semplicemente codifica pigro. Ma poi di nuovo, avere ogni membro ha il proprio getter e/o setter consente di scrivere una documentazione API più completa. Personalmente, di solito uso la tecnica per le classi interne o molto generali. Se tutti i tuoi campi non richiedono alcuna convalida, o ci sono come hai detto diverse decine di loro, allora usare i metodi magici sarebbe accettabile, secondo me.

La linea di fondo è di evitare l'accesso diretto ai membri sulle istanze di classe, punto. Il modo in cui decidi di ottenerlo dipende esclusivamente da te. Assicurati che l'API sia ben documentata, più l'astrazione lo fai.

In una nota finale, in PHP, se si dispone già le classi che vengono utilizzati che non sono incapsulare i loro campi, per esempio qualcosa come

class SomeObject { 
    public $foo; 
    public $bar; 
    public $baz; 
    //... 
} 

si può semplicemente risolvere questa classe senza dover refactoring nulla con qualcosa di simile:

class SomeObject { 
    private $_foo; // having underscore as prefix helps to know what's private/protected 
    private $_bar; // inside the code. 
    private $_baz; 

    public function __get($name) { 
     $methodName = 'get'.ucfirst($name); 
     if (method_exists($this, $methodName)) { 
     return $this->{$methodName}(); 
     } else { 
     throw new Exception("Method '{$methodName}' does not exist"); 
     } 
    } 

    public function __set($name, $value) { 
     $methodName = 'set'.ucfirst($name); 
     if (method_exists($this, $methodName)) { 
     $this->{$methodName}($value); 
     } else { 
     throw new Exception("Method '{$methodName}' does not exist"); 
     } 
    } 

    public function getFoo() { return $this->_foo; } 
    public function setFoo($value) { $this->_foo = $value; } 

    public function getBar() { return $this->_bar; } 
    public function setBar($value) { $this->_bar = $value; } 

    public function getBaz() { return $this->_baz; } 
    public function setBaz($value) { $this->_baz = $value; } 

} 

E poi

$obj = new SomeObject(); 
$obj->foo = 'Hello world'; // legacy code support 
$obj->setFoo('Hello world'); // same thing, but preferred 

E soddisfi sia il paradigma OO che l'accesso diretto agli attributi di un'istanza. Si potrebbe anche avere __call() assegno per il prefisso 'get' o 'set' e chiamare __get() e __set() di conseguenza, ma non vorrei arrivare a tanto, anche se questo sarebbe davvero abilitare le classi di uso generale per l'accesso che i membri privati ​​tramite ->member e ->getMember()/->setMember()

Problemi correlati