読者です 読者をやめる 読者になる 読者になる

DRYな備忘録

Don't Repeat Yourself.

Go言語で、クラス名からインスタンスを動的コンストラクトする、みたいなの【golang】【reflect】

問題

なんか文字列渡ってきてそれに対応するクラスをインスタンス化するみたいなのやったりする。 たとえば

PHP

<?php
class MyKlass {
    public $prop = "foo";
    public function meth() {
        echo "This is my method\n";
        echo "This.prop = {$this->prop}";
    }
}
$class_name = "MyKlass";
$k = new $class_name();
$k->meth();

Python

class MyKlass:
    def __init__(self):
        self.prop = "buz"
    def meth(self):
        print("This prop is " + self.prop)
mod = {
    "MyKlass":MyKlass
}
class_name = "MyKlass"
k = mod[class_name]()
k.meth()

JavaScript

var module = {};
(function(mod){
    var MyKlass = function(){
        this.prop = "foo";
        this.meth = function(){
            console.log("This prop is " + this.prop);
        }
    }
    mod.MyKlass = MyKlass;
})(module);
var className = "MyKlass";
var k = new module[className]();
k.meth();

これ、Go言語でどうやるのかと

調査

やっぱり、返り値の型を抽象的に宣言する(ジェネリクス的な?)方法が無いから、具体レイヤーでその型にキャストするということが絶対に必要になる、という認識でいいのかな

解決

こんな感じ?

package main

import "fmt"
import "reflect"

// structの定義
type MyKlass struct {
    Prop string
}
func (k MyKlass) Meth() {
    fmt.Printf("This prop is %s", k.Prop) 
}

// 型情報を貯めておく
// キー文字列バリュー型情報のmap
var typeRegistry = map[string]reflect.Type{}
// 型情報を貯めるメソッド
func registerType(typ reflect.Type) {
    typeRegistry[typ.String()] = typ
}
// まずmainより前に
// MyKlassという型を登録しとく
func init() {
    registerType(reflect.TypeOf(MyKlass{}))
}
func main() {
    typeName := "main.MyKlass"

    // ちょっと複雑。
    // reflect.Type型を使ってNewして reflect.InterfaceValueをつくる
    // InterfaceValueをElemでValueにする
    // Interfaceメソッドでこれをinterface{}にする
    // interface{}がMyKlassの実装を満たしているか.(type)で調べる
    k,_ := reflect.New(typeRegistry[typeName]).Elem().Interface().(MyKlass)
 
    k.Prop = "foo"
    k.Meth()
}

雑感

うーん、間違ってたら教えてほしい。そして最近つらい。

f:id:otiai10:20140520232333j:plain

DRYな備忘録