Alcuni hanno suggerito che le cose possono sfuggire al controllo usando l'amico. Sono d'accordo, ma ciò non diminuisce la sua utilità. Non sono sicuro che un amico danneggi necessariamente il paradigma OO più che rendere pubblici tutti i membri della tua classe. Certamente la lingua ti permetterà di rendere pubblici tutti i tuoi membri, ma è un programmatore disciplinato che evita quel tipo di modello di progettazione. Allo stesso modo un programmatore disciplinato riserverebbe l'uso dell'amico per casi specifici in cui ha senso. Sento che l'interno espone troppo in alcuni casi. Perché esporre una classe o un metodo a tutto nell'assembly?
Ho una pagina ASP.NET che eredita la mia pagina di base, che a sua volta eredita System.Web.UI.Page. In questa pagina, ho del codice che gestisce la segnalazione degli errori dell'utente finale per l'applicazione in un metodo protetto
ReportError("Uh Oh!");
Ora ho un controllo utente contenuto nella pagina. Voglio che il controllo utente sia in grado di chiamare i metodi di segnalazione errori nella pagina.
MyBasePage bp = Page as MyBasePage;
bp.ReportError("Uh Oh");
Non può farlo se il metodo ReportError è protetto. Posso renderlo interno, ma è esposto a qualsiasi codice nell'assembly. Lo voglio solo esposto agli elementi dell'interfaccia utente che fanno parte della pagina corrente (inclusi i controlli figlio). Più specificamente, desidero che la mia classe di controllo di base definisca esattamente gli stessi metodi di segnalazione degli errori e che chiami semplicemente i metodi nella pagina di base.
protected void ReportError(string str) {
MyBasePage bp = Page as MyBasePage;
bp.ReportError(str);
}
Credo che qualcosa come un amico possa essere utile e implementato nella lingua senza rendere la lingua meno "OO" come, forse come attributi, in modo che tu possa avere classi o metodi amici di classi o metodi specifici, consentendo allo sviluppatore di fornire molto accesso specifico. Forse qualcosa come ... (pseudo codice)
[Friend(B)]
class A {
AMethod() { }
[Friend(C)]
ACMethod() { }
}
class B {
BMethod() { A.AMethod() }
}
class C {
CMethod() { A.ACMethod() }
}
Nel caso del mio esempio precedente forse ho qualcosa di simile al seguente (si può sostenere la semantica, ma sto solo cercando di far passare l'idea):
class BasePage {
[Friend(BaseControl.ReportError(string)]
protected void ReportError(string str) { }
}
class BaseControl {
protected void ReportError(string str) {
MyBasePage bp = Page as MyBasePage;
bp.ReportError(str);
}
}
A mio modo di vedere, il concetto di amico non ha più rischi che renderlo pubblico o creare metodi o proprietà pubblici per accedere ai membri. Semmai un amico consente un altro livello di granularità nell'accessibilità dei dati e consente di restringere tale accessibilità piuttosto che estenderla a livello interno o pubblico.