我读了这个问题有关抽象工厂模式,但唯一的答案是尝试emulate在 Haskell 中就像在 OOP 语言中一样(尽管前言是这样的在 Haskell 中你不需要它).
另一方面,我的目的并不是要在像 Haskell 这样的函数式语言上强制采用特定于 OOP 的模式。恰恰相反,我想理解Haskell 如何满足 OOP 中通过工厂模式解决的需求.
我有一种感觉,即使是这些needs首先在 Haskell 中可能没有意义,但我无法更好地表达这个问题。
我对工厂模式结构的理解(基于这个视频这看起来很清楚)是
- 有很多不同的产品,它们都实现了一个通用的接口
- 有很多不同的创建者类,它们都实现了一个通用接口
- each of the classes in 2 hides a logic to create a product of those in 1
- (如果我理解正确的话,对象和创建者之间没有必要存在一对一的映射,因为创建者可以隐藏不同的逻辑,根据用户输入/时间/条件/任何内容对要创建的特定对象做出不同的选择.)
- 客户端代码将让创建者可以使用,每次使用其中之一时,它(创建者,而不是客户端代码)都会知道如何创建产品以及哪种特定产品。
所有(或部分)这些如何适用于 Haskell?
在 Haskell 中,有几个不同的产品类实现一个通用的产品接口是一件事,接口是一种类型class
,产品类型为(data
s/newtype
s/现有类型)。例如,参考链接视频中的宇宙飞船和小行星示例,我们可以有一个类型class
用于定义Obstacles
任何提供的东西size
, speed
, and position
,
class Obstacle a where
size :: a -> Double
speed :: a -> Int
position :: a -> (Int,Int)
and Asteroid
and Planet
可能是以某种方式实现此接口的两种具体类型,
data Asteroid = Asteroid { eqside :: Double, pos :: (Int,Int) } deriving Show
instance Obstacle Asteroid where
size a = eqside a ^ 3 -- yeah, cubic asteroids
speed _ = 100
position = pos
data Planet = Planet { radius :: Double, center :: (Int,Int) } deriving Show
instance Obstacle Planet where
size a = k * radius a ^ 3
where k = 4.0/3.0*3.14
speed _ = 10
position = center
到目前为止,我没有看到我在 Haskell 和 OOP 语言中所做的事情有任何真正的区别。但接下来就是它了。
此时,按照链接视频中的示例,客户端代码可以是一个穿越某些关卡并根据关卡数量生成不同障碍物的游戏;它可能是这样的:
clientCode :: [Int] -> IO ()
clientCode levels = do
mapM_ (print . makeObstacle) levels
where makeObstacle
应该是创建者函数,或几个给定类型输入的函数之一Int
应用逻辑来选择是否必须创建Asteroid
or a Planet
.
但是我不明白如何拥有一个返回不同类型输出的函数,Asteroid
vs Planet
(事实上他们实现了相同的Obstacle
接口似乎没有帮助),基于相同类型的不同值,[Int]
,更不用说了解“工厂”功能及其通用接口应该是什么了。