问题描述
我已阅读此问题有关抽象工厂模式的信息>,但唯一的答案是尝试在Haskell中模仿在OOP语言中会是什么(尽管前言大致是,您在Haskell中不需要它)
I've read this question about the Abstract factory pattern, but the only answer to it tries to emulate in Haskell what one would in OOP languages (although the forword is along the lines you don't need it in Haskell).
另一方面,我的意图并不是要像Haskell那样在功能语言上强制使用特定于OOP的模式.相反,我想了解 Haskell如何通过Factory模式满足OOP中的需求 .
On the other hand, my intention is not really to force an OOP-specific pattern on a Functional language as Haskell. Quite the opposite, I'd like to understand how Haskell addresses the needs that in OOP are addressed via the Factory pattern.
我有一种感觉,即使这些需求最初在Haskell中可能也没有道理,但我无法更好地提出这个问题.
I have the feeling that even these needs might not make sense in Haskell in the first place, but I can't formulate the question any better.
我对工厂模式结构的理解(基于此视频似乎很清楚)是
My understanding of the structure of the factory pattern (based on this video which seems pretty clear) is that
- 有很多不同的产品,都实现了一个通用的界面
- 有一堆不同的创建者类,都实现了一个公共接口
- 2中的每个类都隐藏了创建1中那些的乘积的逻辑
- (如果我理解正确,对象与创建者之间不必存在一对一的映射关系,因为创建者可以隐藏不同的逻辑,根据用户输入/时间/来选择要创建的特定对象的选择不同条件/任何条件.)
这全部(或部分)如何适用于Haskell?
How does all (or some) of this apply to Haskell?
在Haskell中,有几种不同的产品类实现了通用的产品接口,接口是类型
class
,而产品是类型(data
s/newtype
s/现有类型).例如,参考链接视频中的太空飞船和小行星示例,我们可以使用类型class
来定义Obstacles
可以提供size 的任何物体
,速度
和位置
,Having several different product classes implementing a common product interface is a thing in Haskell, the interface being a type
class
, and the products being types (data
s/newtype
s/existing types). For instance, with reference to the spaceship-and-asteroids example from the linked video, we could have a typeclass
for definingObstacles
anything that providessize
,speed
, andposition
,class Obstacle a where size :: a -> Double speed :: a -> Int position :: a -> (Int,Int)
和
Asteroid
和Planet
可能是通过某种方式实现此接口的两种具体类型,and
Asteroid
andPlanet
could be two concrete types implementing this interface in some way,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语言中所做的任何真正的区别.但是接下来.
So far I don't see any real difference between what I'd do in Haskell or in a OOP language. But it comes next.
在这一点上,按照链接的视频中的示例,客户端代码可以是一个穿越某些关卡并根据关卡数量产生不同障碍的游戏;可能是这样的:
At this point, following the example in the linked video, the client code could be a game that traverses some levels and generates different obstacles based on the number of the level; it could be something like this:
clientCode :: [Int] -> IO () clientCode levels = do mapM_ (print . makeObstacle) levels
其中
makeObstacle
应该是创建者函数,或者是创建者函数之一,给定类型为Int
的输入,它会应用逻辑来选择是否必须创建小行星
或行星
.where
makeObstacle
should be the creator function, or one of several, which given an input of typeInt
applies a logic to chose if it has to create anAsteroid
or aPlanet
.但是我看不到如何有一个函数可以返回不同类型的输出,
Asteroid
与Planet
(实际上它们实现了相同的Obstacle
接口似乎无济于事),基于所有相同类型[Int]
的不同值,更不用说了解工厂"是什么了.函数及其通用接口应该是However I don't see how I can have a function that returns outputs of different types,
Asteroid
vsPlanet
(the fact they implement the sameObstacle
interface doesn't seem to help), based on different values all of the same type,[Int]
, let alone understanding what the "factory" functions and their common interface should be.推荐答案
不完全是.确实,类型类可以表达接口在OO语言中的作用,但这并不总是有意义.具体而言,实际上没有指向所有方法具有
a->形式的类型签名的类的指向.Fubar
.Not quite. It's true that typeclasses can express what interfaces do in OO languages, but this doesn't always make sense. Specifically, there's not really any point to a class where all methods have type signatures of the form
a -> Fubar
.为什么?好吧,您不需要为此的类–只需将其设置为具体的数据类型即可!
Why? Well, you don't need a class for that – just make it a concrete data type!
data Obstacle = Obstace { size :: Double , speed :: Int -- BTW, why would speed be integral?? , position :: (Int,Int) }
记录字段还可以是函数,
IO
操作等,这足以模拟OO类的方法可以完成的工作.普通数据唯一不能表达的是继承–但是即使在OO中,也有一些关于优先于继承而不是继承的主张,就是这样.The record fields can also be functions,
IO
actions, etc. etc. – which is enough to emulate what the methods of an OO class can do. The only thing plain data can't express is inheritance – but even in OO, there's a bit of a mantra about that composition should be preferred over inheritance, so there's that.或者,您可以将
Obstacle
设置为求和类型Alternatively, you could make
Obstacle
a sum typedata Obstacle = Asteroid ... | Planet ...
取决于更好的应用程序.无论哪种方式,它仍然是一种具体的数据类型,不需要任何类.
It depends on the application which is better. Either way it's still a concrete data type, no class required.
由于
Obstacle
是一种简单的数据类型,因此无需对其创建者抽象".相反,您可以简单地使用各种功能->障碍物
会产生障碍,恰好代表小行星,行星或其他任何物体.With
Obstacle
being a simple data type, there's nothing that needs to be "abstract" about it's creators. Instead, you can simply have various functions-> Obstacle
that create obstacles which happen to represent either asteroids, planets, or whatever.您还可以将您的"OO接口实例"包装为数据类型,即 existential
You can also wrap your kind of "OO interface instances" into a data type, an existential
{-# LANGUAGE GADTs #-} class Obstacle a where ... data AnObstacle where AnObstance :: Obstacle a => a -> AnObstacle
但是不要这样做除非您完全知道这就是您想要的.
But don't do that unless you exactly know this is what you want.
这篇关于为什么在Haskell中不需要Factory模式?Haskell如何解决OOP中的模式所解决的需求?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!