2013-06-21 14 views
5

Solo una semplice domanda. Ho visto molti casi in cui il codice viene implementato come il seguente:OOP - utilizzo di proprietà private e public getter

class User 
{ 
    private $id; 
    private $firstname; 
    private $lastname; 

    public function __construct() { some code } 

    public function getId() { return $this->id; } 

    public function getFirstname() { return $this->firstname; } 

    public function setFirstname($value) { $this->firstname = $value; } 
} 

// And properties are accessed like: 
$user->getFirstname(); 
$user->getId(); 

Allora, qual è la ragione di usare le proprietà private e avere getter pubblici, invece di fare le proprietà pubbliche e l'accesso diretto come:

$user->firstname; 

PS: Va bene se utilizzo il secondo metodo?

EDIT

Sembra che io non ho ricerca ben prima di questa domanda (immagino che ho usato tasti sbagliati per cercare l'argomento). Ecco un'altra buona risposta alla (quasi) stessa domanda: https://stackoverflow.com/a/1568230/1075534

Fondamentalmente, per il mio caso, una buona ragione per usare getter e setter è evitare di cambiare 250 classi che accedono direttamente alla proprietà. Così, per esempio, immaginare che non stavo usando un getter:

class User 
{ 
    public $firstname = 'abc'; 
    public $lastname = 'cde'; 
} 

$user = new User(); 

echo $user->firstname . ' ' . $user->lastname; 

Ora, immagino che voglio cambiare il comportamento della mia app e decido di stampare nomi capitalizzati. In questo caso, quindi, cercherei ogni implementazione (250 in questo caso :) e capitalizzerò l'output ovunque avessi chiamato le proprietà. Ma, piuttosto se ho usato un getter poi vorrei solo cambiare il metodo getter:

class User 
{ 
    private $firstname = 'abc'; 
    private $lastname = 'def'; 

    public getFirstname() 
    { 
     return ucfirst(strtolower($this->firstname)); 
    } 

    public getLastname() 
    { 
     return ucfirst(strtolower($this->lastname)); 
    } 
} 

Inoltre, tenere a mente che un getter potrebbe non solo raccogliere informazioni da una singola proprietà. Immaginate la seguente:

class User 
{ 
    private $firstname = 'abc'; 
    private $lastname = 'def'; 

    public function getName() 
    { 
     return $this->firstname . ' ' . $this->lastname; 
    } 
} 

Per coloro che hanno ancora domande su questo argomento, suggerisco loro lettura del materiale che Gordon fornito e soprattutto la risposta che ho linkato.

+0

Si consiglia di leggere codice completo 2. Ha un grande spiegazione su questo argomento e altri concetti di sviluppo del software. – Gohn67

risposta

9

L'utilizzo di Accessors soddisfa Uniform Access Principle.

Citando Wikipedia:

Il principio di accesso uniforme era portata avanti da Bertrand Meyer. Si afferma "Tutti i servizi offerti da un modulo dovrebbero essere disponibili attraverso una notazione uniforme, che non tradisce se sono implementati attraverso lo stoccaggio o attraverso il calcolo." Questo principio si applica in generale ai linguaggi di programmazione orientati agli oggetti. In una forma più semplice, afferma che non dovrebbe esserci differenza tra il lavoro con un attributo, la proprietà precomputed o il metodo/query.

Nel suo blog, Fowler explains

E in effetti significa che un cliente della persona non deve né sapere, né importa se l'età è memorizzato o calcolata. Ciò conferisce alla persona oggetto la flessibilità di cambiare facilmente tra i due e di rimuovere ciò che di solito è una preoccupazione non necessaria da parte del cliente. È una parte importante dell'incapsulamento - o almeno l'aspetto di nascondere i dati dell'incapsulamento.

per illustrare il punto, immaginare una classe come questa:

class Now 
{ 
    public $timestamp; 

    public function getTomorrow() 
    { 
     return strtotime('+1 day', $this->timestamp); 
    } 

// … more code 

Quando si fa in questo modo, si stanno costringendo lo sviluppatore utilizzando tale classe per conoscere i dettagli di implementazione. Per ottenere l'ora timestamp, lo sviluppatore ha a che fare

echo $now->timestamp; 

che, per ottenere il timestamp calcolato per domani, lo sviluppatore ha a che fare

echo $now->getTomorrow(); 

ma lo sviluppatore non dovrebbe aver bisogno di prendersi cura, in modo da se vuoi seguire l'UAP, fornirai un Accessor per ottenere il timestamp. E canalizza tutti gli accessi attraverso i metodi.

Così facendo ha anche l'ulteriore vantaggio di un'API più stabile. Immagina di dover modificare i dettagli di implementazione in un secondo momento nel progetto e trasformare la data/ora in un oggetto DateTime. La classe ora assomiglia a questo:

class Now 
{ 
    public $dateTime; // <-- no longer just a timestamp 

    public function getTomorrow() 
    { 
     return strtotime('+1 day', $this->dateTime->getTimestamp()); 
    } 

// … more code 

Ora qualsiasi sviluppatore quello utilizzato in precedenza $now->timestamp dovranno cambiare il loro codice di consumo per accogliere per questo cambiamento. Mentre quando avevi usato un Getter fin dall'inizio, non sarebbe stato affatto un problema, perché il Getter si sarebbe assicurato che restituisse il timestamp. Per provare ulteriormente quel punto, nota come lo sviluppatore non dovrebbe cambiare nulla per consumare getTomorrow(). Sebbene internamente abbiamo modificato i dettagli, l'API pubblica si comporta ancora allo stesso modo.

Si noti che l'UAP è solo una linea guida. Gli IDE moderni rendono facile la creazione di Accessors e Mutator per voi, quindi è facile da seguire. Ma non è una verità assoluta. Se puoi ragionevolmente giustificare di non seguirlo, non seguirlo e utilizzare proprietà pubbliche. Ma dovrebbe essere una decisione informata.

Tuttavia, in generale, you want to avoid Getters (e Setters) in ogni caso e just tell your objects what to do. Questo darà un'API molto più snella. Se la tua API ha un sacco di Getters, è probabile che stai diffondendo il codice che dovrebbe essere all'interno dell'oggetto nei consumatori.

+1

Questa è davvero una buona risposta. Grazie per la condivisione! –

+0

Grazie a @Gordon, davvero un'ottima risposta. Ho letto tutto il materiale che hai fornito. Ma ho ancora una domanda. Ho principalmente fatto questa domanda per una classe User che stavo per scrivere. Dal momento che può avere molte proprietà (come nome, cognome, email, hash, creato ecc ...), e considerando che decido di seguire il principio UAP, finirò con un sacco di getter e setter. Credo che in questo caso non ci sia niente di male se evito getter e setter, giusto? –

+0

@SavasVedova Se la classe è solo una struttura dati, allora * forse *. Ma ti suggerisco di non domandarti se dovresti seguirlo, ma piuttosto chiedersi perché non lo faresti quando segui le bozze future del tuo codice ed è così facile generarle automaticamente. – Gordon

2

Quando una variabile è dichiarata pubblica, è accessibile da qualsiasi classe rendendola più facile da utilizzare come programmatore; spiegando così il tuo desiderio di usare il secondo metodo che hai descritto. Tuttavia, alcune variabili devono essere dichiarate private per motivi di sicurezza (anche a volte è considerata una buona pratica). Il fatto che li dichiariamo privati ​​li rende accessibili solo nella loro stessa classe. Se tuttavia è necessario utilizzarli in una funzione in un'altra classe, è necessario seguire il primo metodo descritto .

2

utilizzando un getter lo rende di sola lettura.

(questa è l'unica differenza concettuale. Una lingua potrebbe facilmente consentire interfacce da definire in termini di proprietà, nonché i metodi. Convenzioni di denominazione non sono in realtà fondamentali.)

Problemi correlati