La programmation orientée objet d'une manière ou d'une autre est tout à fait possible dans R. Cependant, contrairement à Python par exemple, il existe de nombreuses façons de réaliser l'orientation objet:
Ma question est:
Quelles différences principales distinguent ces façons de OO programmation dans R?
Idéalement, les réponses ici serviront de référence aux programmeurs R essayant de décider quelles méthodes de programmation OO répondent le mieux à leurs besoins).
À ce titre, je demande des détails, présentés de manière objective, fondés sur l'expérience et étayés par des faits et des références. Points bonus pour clarifier comment ces méthodes correspondent aux pratiques standard OO.
Classes S3
print
appelle print.lm
print.anova
, etc. Et s'il n'est pas trouvé, print.default
Classes S4
Classes de référence
proto
Classes R6
Modifier le 3/8/12: La réponse ci-dessous répond à une partie de la question initialement publiée qui a depuis été supprimée. Je l'ai copié ci-dessous, pour fournir le contexte de ma réponse:
Comment les différentes méthodes OO correspondent-elles aux méthodes OO plus standard utilisées par ex. Java ou Python?
Ma contribution se rapporte à votre deuxième question, sur la façon dont les méthodes OO de R correspondent à des méthodes OO plus standard. Comme j'y ai pensé par le passé, je suis revenu encore et encore sur deux passages, l'un de Friedrich Leisch et l'autre de John Chambers. Les deux font un bon travail pour expliquer pourquoi la programmation de type OO dans R a une saveur différente de celle dans de nombreuses autres langues.
Tout d'abord, Friedrich Leisch, de "Création de packages R: un tutoriel" ( avertissement: PDF ):
S est rare car il est à la fois interactif et possède un système d'orientation des objets. Concevoir des classes est clairement de la programmation, mais pour rendre S utile comme environnement interactif d'analyse de données, il est logique que ce soit un langage fonctionnel. Dans les "réels" langages de programmation orientée objet (POO) comme C++ ou Java les définitions de classe et de méthode sont étroitement liées, les méthodes font partie des classes (et donc des objets). Nous voulons des ajouts incrémentiels et interactifs comme des méthodes définies par l'utilisateur pour des classes prédéfinies. Ces ajouts peuvent être effectués à tout moment, même à la volée à l'invite de ligne de commande pendant que nous analysons un ensemble de données. S essaie de faire un compromis entre l'orientation de l'objet et l'utilisation interactive, et bien que les compromis ne soient jamais optimaux par rapport à tous les objectifs qu'ils essaient d'atteindre, ils fonctionnent souvent étonnamment bien dans la pratique.
L'autre passage provient du superbe livre de John Chambers "Software for Data Analysis" . ( Lien vers le passage cité ):
Le modèle de programmation OOP diffère du langage S sur tous les points sauf le premier, même si S et certains autres langages fonctionnels prennent en charge les classes et les méthodes. Les définitions de méthode dans un système OOP sont locales à la classe; il n'est pas nécessaire que le même nom pour une méthode signifie la même chose pour une classe non liée. En revanche, les définitions de méthode dans R ne résident pas dans une définition de classe; conceptuellement, ils sont associés à la fonction générique. Les définitions de classe entrent dans la détermination de la sélection de méthode, directement ou par héritage. Les programmeurs habitués au modèle OOP sont parfois frustrés ou confus que leur programmation ne soit pas transférée directement vers R, mais ce n'est pas le cas. L'utilisation fonctionnelle des méthodes est plus compliquée mais aussi plus adaptée aux fonctions significatives et ne peut pas être réduite à la version OOP.
S3 et S4 semblent être les approches officielles (c'est-à-dire intégrées) pour la programmation OO. J'ai commencé à utiliser une combinaison de S3 avec des fonctions intégrées dans la fonction/méthode constructeur. Mon objectif était d'avoir un object $ method () type syntaxe de sorte que j'ai des champs semi-privés. Je dis semi-privés parce qu'il n'y a aucun moyen de les cacher vraiment (pour autant que je sache). Voici un exemple simple qui ne fait rien :
#' Constructor
EmailClass <- function(name, email) {
nc = list(
name = name,
email = email,
get = function(x) nc[[x]],
set = function(x, value) nc[[x]] <<- value,
props = list(),
history = list(),
getHistory = function() return(nc$history),
getNumMessagesSent = function() return(length(nc$history))
)
#Add a few more methods
nc$sendMail = function(to) {
cat(paste("Sending mail to", to, 'from', nc$email))
h <- nc$history
h[[(length(h)+1)]] <- list(to=to, timestamp=Sys.time())
assign('history', h, envir=nc)
}
nc$addProp = function(name, value) {
p <- nc$props
p[[name]] <- value
assign('props', p, envir=nc)
}
nc <- list2env(nc)
class(nc) <- "EmailClass"
return(nc)
}
#' Define S3 generic method for the print function.
print.EmailClass <- function(x) {
if(class(x) != "EmailClass") stop();
cat(paste(x$get("name"), "'s email address is ", x$get("email"), sep=''))
}
Et un code de test:
test <- EmailClass(name="Jason", "[email protected]")
test$addProp('hello', 'world')
test$props
test
class(test)
str(test)
test$get("name")
test$get("email")
test$set("name", "Heather")
test$get("name")
test
test$sendMail("[email protected]")
test$getHistory()
test$sendMail("[email protected]")
test$getNumMessagesSent()
test2 <- EmailClass("Nobody", "[email protected]")
test2
test2$props
test2$getHistory()
test2$sendMail('[email protected]')
Voici un lien vers un article de blog que j'ai écrit sur cette approche: http://bryer.org/2012/object-oriented-programming-in-r J'accueillerais volontiers vos commentaires, critiques et suggestions à cette approche car je ne suis pas moi-même convaincu si c'est la meilleure approche. Cependant, pour le problème que j'essayais de résoudre, il a très bien fonctionné. Plus précisément, pour le package makeR ( http://jbryer.github.com/makeR ), je ne voulais pas que les utilisateurs modifient directement les champs de données car je devais m'assurer qu'un fichier XML représentant l'état de mon objet resterait synchronisé. Cela a fonctionné parfaitement tant que les utilisateurs respectent les règles que je décris dans la documentation.