Есть ли способ объявить публичные и частные методы для S4 Reference Classes?
Up-front: я am знает, что R - функциональный язык, поэтому, пожалуйста, не кусайте; -)
У меня был опыт большой с использованием подхода ООП для многих моих программ.
Теперь мне интересно, есть ли способ провести различие между публичными и частными методами при использовании S4 Reference Classes в R?
Пример
Определения классов
setRefClass("B",
field=list(
b.1="numeric",
b.2="logical"
),
methods=list(
thisIsPublic=function(...) {
thisIsPublic_ref(.self=.self, ...)
},
thisIsPrivate=function(...) {
thisIsPrivate_ref(.self=.self, ...)
}
)
)
setRefClass("A",
field=list(
a.1="B"
)
)
Примечание
Я обычно не помещаю определение фактического метода в класс def, но разделяю его на метод S4 (т.е. thisIsPublic_ref
) по следующим причинам:
- Таким образом, класс def остается четко организованным и его легче читать в случаях, когда индивидуальный метод defs становится довольно большим.
- Он позволяет в любое время переключиться на функциональное выполнение методов. Be
x
экземпляр определенного класса, вы можете вызвать foo_ref(.self=x)
вместо x$foo()
.
- Это позволяет вам байт-компилировать методы с помощью
compiler::cmpfun()
, который, как я думаю, невозможен, если у вас есть "простые" методы ссылочного класса.
Конечно, не имеет смысла делать это сложным для этого конкретного примера, но я думал, что все же проиллюстрирую этот подход.
Определения методов
setGeneric(
name="thisIsPublic_ref",
signature=c(".self"),
def=function(
.self,
...
) {
standardGeneric("thisIsPublic_ref")
}
)
setGeneric(
name="thisIsPrivate_ref",
signature=c(".self"),
def=function(
.self,
...
) {
standardGeneric("thisIsPrivate_ref")
}
)
require(compiler)
setMethod(
f="thisIsPublic_ref",
signature=signature(.self="B"),
definition=cmpfun(function(
.self,
...
){
.self$b.1 * 1000
})
)
setMethod(
f="thisIsPrivate_ref",
signature=signature(.self="B"),
definition=cmpfun(function(
.self,
...
){
.self$b.2
})
)
Экземпляры
x.b <- new("B", b.1=10, b.2=TRUE)
x.a <- new("A", a.1=x.b, a.2="hello world")
Открытый и закрытый
Для экземпляров класса A
(т.е. x.a
) следует использовать методы класса B
public:
> x.a$a.1$thisIsPublic()
[1] 10000
Экземпляры класса A
(т.е. x.a
) должны иметь не, чтобы использовать методы класса B
private. Поэтому я хотел бы, чтобы этот не работал, то есть приводил к ошибке:
> x.a$a.1$thisIsPrivate()
[1] TRUE
Любая идея, как это можно было бы определить?
Единственное, что я придумал до сих пор:
Добавление аргумента sender
для каждого метода, явно указать его для каждого вызова метода и проверить, есть ли class(.self) == class(sender)
. Но это кажется немного "явным".
Ответы
Ответ 1
Как функции являются первоклассными объектами в R, вы можете вставлять их внутри другого, как показано ниже:
hello <- function() {
print_ <- function() {
return ('hello world')
}
print_()
}
Да, это нахальный, возможно, не самый чистый способ, но он работает... Вызовите использование "hello()".
Ответ 2
Короткий ответ - сделать пакет. R объектные системы, и это означает, что код разбиения (пространства имен) более раздельный, чем их эквиваленты в Java-подобных языках.
Когда вы создаете пакет, вы указываете, что экспортируется в файл NAMESPACE с помощью директив export
и exportMethods
. Вы можете выбрать не экспортировать методы и другие объекты R, которые вы хотите быть приватными (использовать терминологию Java). См. Раздел Пространства имен с разделом классов S4 и методов руководства по расширению Writing R
Создание пакета сложно в первый раз, когда вы это делаете, но там много помощи. См. Документы для package.skeleton и руководство по написанию расширений "Написание R", приведенное выше.
Убедитесь, что классы справки действительно то, что вы хотите. Обычные классы S4 обычно больше R-ish, для чего бы то ни было. Большой источник информации о R многих конструкциях OO (и об упаковке тоже) находится на Hadley Wickham devtools wiki.