По моему мнению ...
Подход 2
Это своего рода побеждает цель принять формальную систему классов, а затем создать класс, который содержит плохо определенные слоты ('A 'или NULL).Как минимум, я бы попытался сделать так, чтобы DataClass1 имел значение, подобное NULL.В качестве простого примера здесь по умолчанию используется числовой вектор нулевой длины.
setClass("DataClass1", representation=representation(x="numeric"))
DataClass1 <- function(x=numeric(), ...) {
new("DataClass1", x=x, ...)
}
Тогда
setClass("MasterClass1", representation=representation(dataClass1="DataClass1"))
MasterClass1 <- function(dataClass1=DataClass1(), ...) {
new("MasterClass1", dataClass1=dataClass1, ...)
}
Одним из преимуществ этого является то, что методы не должны проверять, является лиЭкземпляр в слоте имеет значение NULL или 'DataClass1'
setMethod(length, "DataClass1", function(x) length(x@x))
setMethod(length, "MasterClass1", function(x) length(x@dataClass1))
> length(MasterClass1())
[1] 0
> length(MasterClass1(DataClass1(1:5)))
[1] 5
В ответ на ваш комментарий о предупреждении пользователей при доступе к «пустым» слотам и о том, что пользователи обычно хотят, чтобы функции что-то сделали, а не сказали им, что ониесли вы делаете что-то не так, я бы, вероятно, вернул пустой объект DataClass1()
, который точно отражает состояние объекта.Возможно, метод show
предоставит обзор, который укрепит статус слота - DataClass1: нет.Это представляется особенно уместным, если MasterClass1 представляет собой способ координации нескольких различных анализов, из которых пользователь может выполнять только некоторые.
Ограничением этого подхода (или вашего подхода 2) является то, что вы не получаете диспетчеризацию метода- вы не можете написать методы, которые подходят только для экземпляра с DataClass1
экземплярами, которые имеют ненулевую длину и вынуждены выполнять какую-то ручную диспетчеризацию (например, с if
или switch
).Это может показаться ограничением для разработчика, но это также относится и к пользователю - пользователь не понимает, какие операции являются уникально подходящими для экземпляров MasterClass1, имеющих экземпляры DataClass1 ненулевой длины.
Подход 1
Когда вы говорите, что имена классов в иерархии вводят в заблуждение вашего пользователя, кажется, что это, возможно, указывает на более фундаментальную проблему - вы тоже пытаетесьтрудно сделать всестороннее представление типов данных;пользователь никогда не сможет отслеживать ClassWithMatrixDataFrameAndTree, поскольку он не отображает способ просмотра данных.Это, возможно, возможность уменьшить свои амбиции, чтобы по-настоящему заняться только наиболее заметными частями области, которую вы исследуете.Или, возможно, возможность переосмыслить, как пользователь может думать и взаимодействовать с собранными им данными, и использовать отделение интерфейса (то, что видит пользователь) от реализации (как вы решили представлять данные вклассы), предоставляемые системами классов для более эффективной инкапсуляции того, что пользователь, вероятно, будет делать.
Если оставить в стороне наименование и количество классов, когда вы говорите «трудно расширять для дополнительных типов данных в будущем», это делаетмне интересно, возможно, некоторые нюансы классов S4 сбивают вас с толку?Короткое решение состоит в том, чтобы избежать написания собственных методов initialize
и полагаться на конструкторы для выполнения хитрой работы в соответствии с
setClass("A", representation(x="numeric"))
setClass("B", representation(y="numeric"), contains="A")
A <- function(x = numeric(), ...) new("A", x=x, ...)
B <- function(a = A(), y = numeric(), ...) new("B", a, y=y, ...)
, а затем
> B(A(1:5), 10)
An object of class "B"
Slot "y":
[1] 10
Slot "x":
[1] 1 2 3 4 5