为什么 Haskell 中不需要工厂模式? OOP 中的模式解决的需求在 Haskell 中是如何解决的?

2023-11-23

我读了这个问题有关抽象工厂模式,但唯一的答案是尝试emulate在 Haskell 中就像在 OOP 语言中一样(尽管前言是这样的在 Haskell 中你不需要它).

另一方面,我的目的并不是要在像 Haskell 这样的函数式语言上强制采用特定于 OOP 的模式。恰恰相反,我想理解Haskell 如何满足 OOP 中通过工厂模式解决的需求.

我有一种感觉,即使是这些needs首先在 Haskell 中可能没有意义,但我无法更好地表达这个问题。

我对工厂模式结构的理解(基于这个视频这看起来很清楚)是

  1. 有很多不同的产品,它们都实现了一个通用的接口
  2. 有很多不同的创建者类,它们都实现了一个通用接口
  • each of the classes in 2 hides a logic to create a product of those in 1
    • (如果我理解正确的话,对象和创建者之间没有必要存在一对一的映射,因为创建者可以隐藏不同的逻辑,根据用户输入/时间/条件/任何内容对要创建的特定对象做出不同的选择.)
  • 客户端代码将让创建者可以使用,每次使用其中之一时,它(创建者,而不是客户端代码)都会知道如何创建产品以及哪种特定产品。

所有(或部分)这些如何适用于 Haskell?

在 Haskell 中,有几个不同的产品类实现一个通用的产品接口是一件事,接口是一种类型class,产品类型为(datas/newtypes/现有类型)。例如,参考链接视频中的宇宙飞船和小行星示例,我们可以有一个类型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],更不用说了解“工厂”功能及其通用接口应该是什么了。


在 Haskell 中,有几个不同的产品类实现一个通用的产品接口是一件事,该接口是一个类型类

不完全的。确实,类型类可以表达 OO 语言中接口的功能,但这并不总是有意义。具体来说,对于所有方法都具有以下形式的类型签名的类来说,实际上没有任何意义a -> Fubar.

为什么?好吧,您不需要为此创建一个类 - 只需将其设置为具体的数据类型即可!

data Obstacle = Obstace
  { size :: Double
  , speed :: Int      -- BTW, why would speed be integral??
  , position :: (Int,Int) }

记录字段也可以是函数,IO动作等等——这足以模拟 OO 类的方法可以做什么。普通数据唯一无法表达的是继承——但即使在面向对象中,也有一些关于组合应该优先于继承的口头禅,所以就是这样。

或者,你可以使Obstacle总和类型

data Obstacle = Asteroid ...
              | Planet ...

这取决于哪个更好的应用程序。不管怎样,它仍然是一个具体的数据类型,不需要类。

With Obstacle作为一种简单的数据类型,它的创建者不需要“抽象”。相反,您可以简单地拥有各种功能-> Obstacle这些障碍物恰好代表小行星、行星或其他什么。

您还可以将您的“OO 接口实例”包装到数据类型中,存在主义的

{-# LANGUAGE GADTs #-}

class Obstacle a where ...

data AnObstacle where
  AnObstance :: Obstacle a => a -> AnObstacle

But 不要那样做除非你确切地知道这就是你想要的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 Haskell 中不需要工厂模式? OOP 中的模式解决的需求在 Haskell 中是如何解决的? 的相关文章

随机推荐