Некоторые предполагают, что вещи могут выйти из-под контроля с помощью друга. Я бы согласился, но это не умаляет его полезности. Я не уверен, что друг обязательно повредит парадигме ОО больше, чем обнародует всех ваших учеников. Конечно, язык позволит вам сделать всех ваших участников публичными, но это дисциплинированный программист, который избегает такого типа шаблона проектирования. Точно так же дисциплинированный программист зарезервировал бы использование друга для определенных случаев, где это имеет смысл. Я чувствую, что внутреннее разоблачение слишком много в некоторых случаях. Зачем выставлять класс или метод всему в сборке?
У меня есть страница ASP.NET, которая наследует мою собственную базовую страницу, которая, в свою очередь, наследует System.Web.UI.Page. На этой странице у меня есть некоторый код, который обрабатывает отчеты об ошибках конечного пользователя для приложения в защищенном методе
ReportError("Uh Oh!");
Теперь у меня есть пользовательский элемент управления, который содержится на странице. Я хочу, чтобы пользовательский элемент управления мог вызывать методы сообщения об ошибках на странице.
MyBasePage bp = Page as MyBasePage;
bp.ReportError("Uh Oh");
Это не может быть сделано, если метод ReportError защищен. Я могу сделать его внутренним, но он подвергается воздействию любого кода в сборке. Я просто хочу, чтобы он был открыт для элементов пользовательского интерфейса, которые являются частью текущей страницы (включая дочерние элементы управления). В частности, я хочу, чтобы мой базовый класс управления определял те же методы сообщения об ошибках и просто вызывал методы на базовой странице.
protected void ReportError(string str) {
MyBasePage bp = Page as MyBasePage;
bp.ReportError(str);
}
Я считаю, что что-то вроде друга может быть полезным и реализовано в языке, не делая язык менее "ОО", как, например, как атрибуты, так что вы можете иметь классы или методы, дружить с конкретными классами или методами, что позволяет разработчику конкретный доступ. Возможно, что-то вроде ... (псевдокод)
[Friend(B)]
class A {
AMethod() { }
[Friend(C)]
ACMethod() { }
}
class B {
BMethod() { A.AMethod() }
}
class C {
CMethod() { A.ACMethod() }
}
В моем предыдущем примере, возможно, есть что-то вроде следующего (можно поспорить с семантикой, но я просто пытаюсь донести идею):
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);
}
}
На мой взгляд, концепция «друг» несет в себе не больший риск, чем обнародование информации или создание открытых методов или свойств для доступа к членам. Во всяком случае, друг допускает другой уровень детализации в доступности данных и позволяет вам сузить эту доступность, а не расширять ее внутренними или общедоступными.