本文介绍了为什么在Haskell中不需要Factory模式?Haskell如何解决OOP中的模式所解决的需求?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已阅读此问题有关抽象工厂模式的信息>,但唯一的答案是尝试在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

  1. 有很多不同的产品,都实现了一个通用的界面
  2. 有一堆不同的创建者类,都实现了一个公共接口

  • 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 typeclass, and the products being types (datas/newtypes/existing types). For instance, with reference to the spaceship-and-asteroids example from the linked video, we could have a typeclass for defining Obstacles anything that provides size, speed, and position,

      class Obstacle a where
        size :: a -> Double
        speed :: a -> Int
        position :: a -> (Int,Int)
      

      Asteroid Planet 可能是通过某种方式实现此接口的两种具体类型,

      and Asteroid and Planet 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 type Int applies a logic to chose if it has to create an Asteroid or a Planet.

      但是我看不到如何有一个函数可以返回不同类型的输出, Asteroid Planet (实际上它们实现了相同的 Obstacle接口似乎无济于事),基于所有相同类型 [Int] 的不同值,更不用说了解工厂"是什么了.函数及其通用接口应该是

      However I don't see how I can have a function that returns outputs of different types, Asteroid vs Planet (the fact they implement the same Obstacle 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 type

      data 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中的模式所解决的需求?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-30 06:22