1

I like to change the the folowing src in that way, that the initialize function looks like certain contructors. I would like to change the folowing example

    Part.initialize<-function(...){
  args<-list(...)
  if(all(class(args[[1]])==c("XMLInternalElementNode", "XMLInternalNode", "XMLAbstractNode"))){
    attr<-xmlAttrs(node)
    .self$var1 <- if(is.na(attr["var1"])) vector() else attr["var1"]
    .self$var2 <- if(is.na(attr["var2"])) character() else as.character(attr["var2"])
  }else{
    .self$var1 <- if(is.null(args[["var1"]])) vector() else args[["var1"]]
    .self$var2 <- if(is.null(args[["var2"]])) character() else as.character(args[["var2"]])
  }
  .self
}

Part<-setRefClass(Class = "Part"
                 ,fields = c(var1 = "ANY", var2 = "character")
                 ,methods = list(initialize=Part.initialize))

txt = "<doc> <part var2=\"abc\"/> </doc>"

tree <- xmlTreeParse(txt, useInternalNodes = TRUE)
node<-xpathSApply(tree, "//part")[[1]]
part <- Part$new(node)

to something like:

 Part.initialize<-function(XMLNode){
        do something
   }
   Part.initialize<-function(var1=c(),var2=character()){
        do something
   }

Besides how to handle default value for type ANY? Till now I use vector().

Klaus
  • 1,946
  • 3
  • 19
  • 34

1 Answers1

2

Do not write an initialize method, write a public constructor (.Part: a constructor to be used by your code only, not the user). The public constructor's job is to transform user arguments to a consistent form for class methods

.Part<-setRefClass(Class = "Part"
                  ,fields = c(var1 = "ANY", var2 = "character"))

Use setOldClass to enable class dispatch

setOldClass(c("XMLInternalElementNode", "XMLInternalNode",
              "XMLAbstractNode"))

Write your public constructor as an S4 generic and methods

setGeneric("Part", function(x, ...) standardGeneric("Part"))

setMethod("Part", "missing", function(x, ...) {
    .Part()
})

setMethod("Part", "XMLInternalNode", function(x, ...) {
    attr<-xmlAttrs(x)
    var1 <- if (!is.na(attr["var1"])) attr["var1"] else vector()
    var2 <- if (!is.na(attr["var2"])) attr["var2"] else character()
    .Part(var1=var1, var2=var2, ...)
})

setMethod("Part", "ANY", function(x, var2, ...) {
    .Part(var1=x, var2=var2, ...)
})

Add a copy constructor if desired

setMethod("Part", "Part", function(x, ...) x$copy())

or if your own initialize method does something additional and conforms to the contract of the default initialize method (which acts as a copy constructor too) use

setMethod("Part", "Part", function(x, ...) .Part(x, ...))

Add any common code shared by constructors to the initialize method, being sure that your initialize method acts as a copy constructor and works when invoked without any arguments.

Make sure that simple test cases work

library(XML)
Part()
Part(TRUE, "var2")
txt <- "<doc> <part var2=\"abc\"/> </doc>"
node <- xmlTreeParse(txt, useInternalNodes = TRUE)[["//part"]]
p1 <- Part(node)
p2 <- Part(p1)
p1$var2 <- "xyz"
p2$var2            ## "abc"
Martin Morgan
  • 45,935
  • 7
  • 84
  • 112
  • Many thx, is it redundant to combine it with a initialize method in the folowing way? ` generic.initialize<-function(...){ args <- list(...); map <- .self$getRefClass()$fields(); map <- map[map != "ANY"]; idx <- names(args) %in% names(map); args[idx] <- Map(as, args[idx], map[names(args)[idx]]); do.call(.self$initFields, args); callSuper(...); } .Part <- setRefClass(Class = "Part", fields=c(var0 = "ANY", var1 = "numeric", var2 = "character"), methods=list(initialize=generic.initialize)) setMethod("Part", "XMLInternalNode", function(x, ...){ attr<-xmlAttrs(x); do.call(.Part, as.list(attr)); })` – Klaus Sep 23 '13 at 06:56
  • A problem which comes with this proceeder is that I cant call member function (.self$foo) in the constructor. How could I handle this? – Klaus Sep 23 '13 at 11:20
  • How to create an copy of an object? setMethod("Part", "Part", function(x, ...) { .Part(var1=x$var1, var2=x$var2, ...) }) or should I use `x$copy()`? – Klaus Sep 23 '13 at 12:06
  • Thx, and what you suggest for calling member function in a constructer? – Klaus Sep 23 '13 at 13:20
  • Define a plain-old-function as a helper (perhaps also invoked by a class method) or do the class-specific initialization in the initialize method. – Martin Morgan Sep 23 '13 at 13:50
  • "Define a plain-old-function" you mean similar what a static method is in Java. "or do the class-specific initialization in the initialize method." but then I have to do the case decisions in the initialize methode. – Klaus Sep 23 '13 at 14:32
  • Sorry, @Klaus, I can't help you further! – Martin Morgan Sep 23 '13 at 15:00
  • Thx alot for your help, is there no way to get this OOP style in a properly way to R, or is my question unclear? – Klaus Sep 23 '13 at 15:47
  • You're looking for an overall design, with trade-offs between different approaches. Some of the trade-offs are technical, some reflect decisions you've already made, some represent mis-understandings (on your part and on mine). It's too complicated for me to address these issues all at once and in this forum. Sorry. – Martin Morgan Sep 23 '13 at 16:30
  • how do you organize your src-code in R, if it should look like oop? Would you realy use a construct like we discussed here? – Klaus Sep 23 '13 at 17:46