为什么 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 中是如何解决的? 的相关文章

  • 用于遇到 [...] 的 Haskell Parsec 解析器

    我正在尝试使用 Parsec 在 Haskell 中编写一个解析器 目前我有一个可以解析的程序 test x 1 2 3 end 执行此操作的代码如下 testParser do reserved test v lt identifier
  • Javascript:我应该隐藏我的实现吗?

    作为一名 C 程序员 我有一个习惯 将可以而且应该私有的东西设为私有 当 JS 类型向我公开其所有私有部分时 我总是有一种奇怪的感觉 而且这种感觉并没有被 唤起 假设我有一个类型draw方法 内部调用drawBackground and d
  • 如何在haskell中获取变量名称

    我来到 haskell 时有一些 c 背景知识 想知道是否有类似的 define print a printf s d n a a int a 5 print a 应该打印 a 5 这是 augustss 提到的 TH 解决方案 LANGU
  • 静态库中的单例类

    假设我在静态库中有一个单例类 S 它可以与其他动态库 D1 D2 D3 链接 因此 据我了解 类 S 在每个 D1 D2 和 D3 中都会有一个单独的实例 即使它不是单例 如全局 这也是正确的 有什么办法可以防止S类的多副本吗 我无法将单例
  • 找不到模块“Yesod”

    我有以下代码 LANGUAGE TypeFamilies QuasiQuotes MultiParamTypeClasses TemplateHaskell OverloadedStrings module Simple where imp
  • IoC 服务的抽象类还是接口?

    我目前正在使用 IoC 在项目中提供存储库的具体实现 我读过的所有示例都使用接口作为服务的定义 但是 在阅读了 Microsoft 的建议后 建议更喜欢抽象类而不是接口 http msdn microsoft com en us libra
  • Haskell:IORef 的性能

    我一直在尝试在 Haskell 中编码一个需要使用大量可变引用的算法 但与纯粹的惰性代码相比 它 也许并不奇怪 非常慢 考虑一个非常简单的例子 module Main where import Data IORef import Contr
  • Haskell - lambda 表达式

    我试图了解什么是有用的以及如何在 Haskell 中实际使用 lambda 表达式 我不太明白使用 lambda 表达式相对于定义函数的约定方式有何优势 例如 我通常会执行以下操作 let add x y x y 我可以简单地打电话 add
  • Haskell 输入返回元组

    我想知道 IO 函数是否可以返回元组 因为我想从这个函数中获取这些元组作为另一个函数的输入 investinput IO gt Char Int investinput do putStrLn Enter Username username
  • Haskell 对于 Web 应用程序来说足够成熟吗? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 使用 NSError 检查错误的正确结构

    我正在编写各种例程 并尽力保持其整洁和重构 我正在创建的方法开始看起来与此代码类似 IBAction buttonPress id sender Create Document Shopping List with this documen
  • 在javascript中访问函数内的实例变量?

    如何以最简单的方式访问函数内的实例变量 function MyObject Instance variables this handler Methods this enableHandler function var button doc
  • 什么是对象序列化和反序列化?

    什么是对象序列化 and 反序列化 序列化与读取对象的属性 然后用它们填充 DataRow 的列 最后将 DataRow 保存在数据库中等普通技术有什么区别 序列化通常是指创建可用于存储 可能在文件中 通过网络传输或仅用于进程之间传输的数据
  • UML类图:抽象方法和属性是这样写的吗?

    当我第一次为一个小型 C 项目创建 uml 类图时 我在属性方面遇到了一些麻烦 最后我只是将属性添加为变量 lt
  • 如何在不声明新数据的情况下更改类型(String,Int)元组的 Ord 实例?

    我正在尝试对类型列表进行排序 String Int 默认情况下 它按字符串排序 然后按整数排序 如果字符串相等 我希望它是相反的 首先比较整数 然后如果相等则比较字符串 另外 我不想切换到 Int String 我找到了一种通过定义实例来实
  • 如何在不改变也不重新分配的情况下实现可设置和可检索的状态?

    编写代码时可以遵循以下几条规则 当没有重新分配时 代码更容易阅读和推理 许多 linter 推荐首选const只要有可能 代码也更容易阅读和推理对象何时不会发生变化 如果您在代码的一部分中定义了一个对象 那么知道您可以在其他地方自由引用该对
  • 管道:多个流消费者

    我编写了一个程序来计算语料库中 NGram 的频率 我已经有一个函数 它消耗一串令牌并生成一个订单的 NGram ngram Monad m gt Int gt Conduit t m t trigrams ngram 3 countFre
  • 如何在Haskell中实现词法分析器和解析器

    我在这里得到了这段代码 它是用Haskell结构的命令式编程语言编写的程序 所以问题是 我如何为这种语言实现词法分析器和解析器 该程序被定义为一系列语句有 6 种类型 goto write stop if goto 和 int int n
  • Java中接口作为方法参数

    前几天去面试 被问到了这样的问题 问 反转链表 给出以下代码 public class ReverseList interface NodeList int getItem NodeList nextNode void reverse No
  • C# 中的接口继承

    我试图解决我在编写应用程序时遇到的相当大的 对我来说 问题 请看这个 为了简单起见 我将尝试缩短代码 我有一个名为的根接口IRepository

随机推荐