実際の質問
R6が多重継承をサポートしていないという事実を回避するためのオプションは何ですか?
免責事項
R は基本的に関数型言語であることは知っています。ただし、非常に強力なオブジェクト指向も組み込まれています。さらに、OOD の原則/動作を模倣することの何が問題なのかわかりません。
C#、Java などのオブジェクト指向言語のプロトタイピングを行っていることを知っています。
アプリのプロトタイプは、自己完結型である必要があります(DB バックエンド、ビジネス ロジック、フロントエンド/UI を含む「フル スタック」)。
あなたはR6のような素晴らしい「プロトタイピング技術」を持っていて、自由に使うことができます
環境
Web アプリ用の私の R プロトタイプは、「フル スタック」/自己完結型であり、本番環境言語( C#/.ネット)。
その点で、コード モジュールを切り離し、OOD の SOLID 原則のD (依存性反転の原則)に準拠するために、インターフェイス (または抽象クラス) の使用が非常に好きになりました( 「ボブおじさん」による詳細な説明)。 )。
R6 はインターフェイスを明示的にサポートしていませんが、「抽象メソッド」のみを定義する R6 クラスで完全に模倣できます(以下の例を参照)。これは、R にあまり詳しくない OO プログラマーに私のソフトウェア設計を伝えるのに非常に役立ちます。
ただし、実際に他の具象から継承したい場合(「 abstractのような」模倣されたインターフェイス クラスとは対照的に) には、少し問題になるinherit
inの値を放棄する必要があります。しかし、 の 2 つのクラス。R6Class
inherit
例
依存関係の反転前:
Foo
具体的なクラスに依存しBar
ます。OOD 原則の観点からは、コードが密結合になるため、これはかなり悪いことです。
Bar <- R6Class("Bar",
public = list(doSomething = function(n) private$x[1:n]),
private = list(x = letters)
)
Foo <- R6Class("Foo",
public = list(bar = Bar$new())
)
inst <- Foo$new()
> class(inst)
> class(inst$bar)
[1] "Bar" "R6"
依存関係の反転後:
Foo
Bar
現在は切り離されています。どちらも、 class によって模倣されるインターフェースに依存していますIBar
。実行時に のインスタンスにプラグインするインターフェイスの実装を決定できFoo
ます (プロパティ インジェクション: のフィールドbar
によって実現Foo
)
IBar <- R6Class("IBar",
public = list(doSomething = function(n = 1) stop("I'm the inferace method"))
)
Bar <- R6Class("Bar", inherit = IBar,
public = list(doSomething = function(n = 1) private$x[1:n]),
private = list(x = letters)
)
Baz <- R6Class("Baz", inherit = IBar,
public = list(doSomething = function(n = 1) private$x[1:n]),
private = list(x = 1:24)
)
Foo <- R6Class("Foo",
public = list(bar = IBar$new())
)
inst <- Foo$new()
inst$bar <- Bar$new()
> class(inst$bar)
[1] "Bar" "IBar" "R6"
> inst$bar$doSomething(5)
[1] "a" "b" "c" "d" "e"
inst$bar <- Baz$new()
[1] "Baz" "IBar" "R6"
> inst$bar$doSomething(5)
[1] 1 2 3 4 5
これがOODに関して理にかなっている理由について少し詳しく説明します。フィールドに格納されたオブジェクトが実装Foo
される方法に完全に依存しない必要があります。知る必要があるのは、そのオブジェクトで呼び出すことができるメソッドだけです。それを知るには、 field 内のオブジェクトが実装するインターフェース(私たちの場合は method ) を知るだけで十分です。bar
bar
IBar
doSomething()
基本クラスからの継承を使用して設計を簡素化する:
ここまでは順調ですね。ただし、他の具象クラスの一部が継承できる特定の具象基本クラスを定義することで、設計を簡素化したいとも考えています。
BaseClass <- R6Class("BaseClass",
public = list(doSomething = function(n = 1) private$x[1:n])
)
Bar <- R6Class("Bar", inherit = BaseClass,
private = list(x = letters)
)
Baz <- R6Class("Bar", inherit = BaseClass,
private = list(x = 1:24)
)
inst <- Foo$new()
inst$bar <- Bar$new()
> class(inst$bar)
[1] "Bar" "BaseClass" "R6"
> inst$bar$doSomething(5)
[1] "a" "b" "c" "d" "e"
inst$bar <- Baz$new()
> class(inst$bar)
[1] "Baz" "BaseClass" "R6"
> inst$bar$doSomething(5)
[1] 1 2 3 4 5
「インターフェースの実装」と基本クラスの継承を組み合わせる:
これは、多重継承が必要な場所なので、次のようなものが機能します (PSEUDO CODE):
IBar <- R6Class("IBar",
public = list(doSomething = function() stop("I'm the inferace method"))
)
BaseClass <- R6Class("BaseClass",
public = list(doSomething = function(n = 1) private$x[1:n])
)
Bar <- R6Class("Bar", inherit = c(IBar, BaseClass),
private = list(x = letters)
)
inst <- Foo$new()
inst$bar <- Bar$new()
class(inst$bar)
[1] "Bar" "BaseClass" "IBar" "R6"
現在、私の値inherit
は、インターフェイスの実装を模倣するために「ちょうど」使い果たされているため、実際の具象クラスの継承の「実際の」利点を失います。
別の考え:
あるいは、インターフェイス と具象クラスの区別を何らかの形で明示的にサポートすることは素晴らしいことです。たとえば、このようなもの
Bar <- R6Class("Bar", implement = IBar, inherit = BaseClass,
private = list(x = letters)
)