1. Ruby(直感的・シンプル)
Rubyではクラスがそのままオブジェクトなので、非常にスッキリ書けます。
Ruby
class PaymentFactory
def self.create(type)
case type
when :credit then CreditPayment.new
when :cash then CashPayment.new
end
end
end
利用側:クラス名すら知らなくていい
payment = PaymentFactory.create(:credit)
payment.pay(100)
「newして返すだけ」という直感。リファクタリングもクラスの中身を書き換えるだけで済みます。]
2. Go(構造体が必要な「あいたた」例)
Goにはクラスがないため、**「メソッドをまとめるための構造体」を定義し、さらに「インターフェース」**を定義する必要があります。
Go
type Payment interface {
Pay(amount int)
}
// メソッドしか必要なくても、空の構造体が必要
type CreditPayment struct{}
func (p CreditPayment) Pay(amount int) { /* ... */ }
type CashPayment struct{}
func (p CashPayment) Pay(amount int) { /* ... */ }
// Factory関数
func NewPayment(t string) Payment {
switch t {
case "credit":
return CreditPayment{} // 構造体のインスタンス化が必要
case "cash":
return CashPayment{}
default:
return nil
}
}
「関数だけでいいのに、インターフェースを満たすために空の構造体を作らされる」という手間が発生します。
3. Rust(型と所有権の壁)
Rustはさらに厳格です。Factoryから「動的に異なる型」を返す場合、メモリサイズを確定させるために Box(ポインタ)が必要になります。
Rust
trait Payment {
fn pay(&self, amount: u32);
}
struct CreditPayment; // 空の構造体
impl Payment for CreditPayment {
fn pay(&self, amount: u32) { /* ... */ }
}
struct CashPayment;
impl Payment for CashPayment {
fn pay(&self, amount: u32) { /* ... */ }
}
// Factory:Box という魔法の呪文が必要
fn create_payment(t: &str) -> Box {
match t {
"credit" => Box::new(CreditPayment),
"cash" => Box::new(CashPayment),
_ => panic!("Unknown"),
}
}
プログラムはドメインの機能に集中すべきであり、なるべくシンプルな方法を選ぶべきで余計なことに気を使う必要はない