代码是如何变得抽象?

从简单的代码开始,慢慢的迭代,经过几次变得抽象复杂起来。
本文将以定时任务系统 worker 的演化过程来说明代码是如何变得抽象。

第一次迭代

module Worker2 where

import Prelude

import Effect (Effect)
import Effect.Aff (Aff, delay, launchAff_)
import Effect.Aff.Class (liftAff)
import Data.Time.Duration (Milliseconds (..))
import Periodic.Worker (addFunc, done, name, runWorkerT, work)

periodicHost :: {port :: Int, host :: String}
periodicHost = {port: 5000, host: "127.0.0.1"}

someFunc1 :: String -> Aff Unit
someFunc1 _ = delay (Milliseconds 1000.0)

main :: Effect Unit
main = launchAff_ $ runWorkerT launchAff_ periodicHost do
  addFunc "func1" do
    n <- name
    liftAff $ someFunc1 n
    done

  work 10

第二次迭代

module Worker2 where

import Prelude

import Effect (Effect)
import Effect.Aff (Aff, delay, launchAff_)
import Effect.Aff.Class (liftAff)
import Data.Time.Duration (Milliseconds (..))
import Periodic.Worker (addFunc, done, name, runWorkerT, work, JobT)

periodicHost :: {port :: Int, host :: String}
periodicHost = {port: 5000, host: "127.0.0.1"}

someFunc1 :: String -> Aff Unit
someFunc1 _ = delay (Milliseconds 1000.0)

someFunc2 :: String -> Aff Unit
someFunc2 _ = delay (Milliseconds 1000.0)

affTask :: (String -> Aff Unit) -> JobT Aff Unit
affTask f = do
  n <- name
  liftAff $ f n
  done

main :: Effect Unit
main = launchAff_ $ runWorkerT launchAff_ periodicHost do
  addFunc "func1" $ affTask someFunc1
  addFunc "func2" $ affTask someFunc2

  work 10

func1func2 是差不多类似的函数所以我们抽象出 affTask

第三次迭代

affTask 目前只能适用 String -> Aff Unit 类型的函数,
但实际上我们会有很多相类似的函数,如 Int -> Aff Unit
我们需要对 affTask 进行改造,让它适用。

module Worker2 where

import Prelude

import Effect (Effect)
import Effect.Aff (Aff, delay, launchAff_)
import Effect.Aff.Class (liftAff)
import Data.Time.Duration (Milliseconds (..))
import Periodic.Worker (addFunc, done, name, runWorkerT, work, JobT)
import Data.Maybe (Maybe (..))
import Data.Int (fromString)

periodicHost :: {port :: Int, host :: String}
periodicHost = {port: 5000, host: "127.0.0.1"}

someFunc1 :: String -> Aff Unit
someFunc1 _ = delay (Milliseconds 1000.0)

someFunc2 :: String -> Aff Unit
someFunc2 _ = delay (Milliseconds 1000.0)

someFunc3 :: Int -> Aff Unit
someFunc3 _ = delay (Milliseconds 1000.0)

mkAffTask :: forall a. (String -> Maybe a) -> (a -> Aff Unit) -> JobT Aff Unit
mkAffTask mk f = do
  n <- mk <$> name
  case n of
    Nothing -> done
    Just n0 -> do
      liftAff $ f n0
      done

affTask :: (String -> Aff Unit) -> JobT Aff Unit
affTask = mkAffTask Just

affTaskI :: (Int -> Aff Unit) -> JobT Aff Unit
affTaskI = mkAffTask fromString

main :: Effect Unit
main = launchAff_ $ runWorkerT launchAff_ periodicHost do
  addFunc "func1" $ affTask someFunc1
  addFunc "func2" $ affTask someFunc2
  addFunc "func3" $ affTaskI someFunc3

  work 10

mkAffTask 就是用来构造 affTaskaffTaskI 这两个函数。

第四次迭代

如果我们的函数需要在过一段时间在执行一个呢?

module Worker2 where

import Prelude

import Effect (Effect)
import Effect.Aff (Aff, delay, launchAff_)
import Effect.Aff.Class (liftAff)
import Data.Time.Duration (Milliseconds (..))
import Periodic.Worker (addFunc, done, name, runWorkerT, work, JobT, schedLater)
import Data.Maybe (Maybe (..))
import Data.Int (fromString)

periodicHost :: {port :: Int, host :: String}
periodicHost = {port: 5000, host: "127.0.0.1"}

someFunc1 :: String -> Aff Unit
someFunc1 _ = delay (Milliseconds 1000.0)

someFunc2 :: String -> Aff Unit
someFunc2 _ = delay (Milliseconds 1000.0)

someFunc3 :: Int -> Aff Unit
someFunc3 _ = delay (Milliseconds 1000.0)

someFunc4 :: String -> Aff (Maybe Int)
someFunc4 _ = pure $ Just 10

someFunc5 :: Int -> Aff (Maybe Int)
someFunc5 _ = pure Nothing

mkAffTask :: forall a b. (b -> JobT Aff Unit) -> (String -> Maybe a) -> (a -> Aff b) -> JobT Aff Unit
mkAffTask finish mk f = do
  n <- mk <$> name
  case n of
    Nothing -> done
    Just n0 -> do
      r <- liftAff $ f n0
      finish r

maybeLater :: Maybe Int -> JobT Aff Unit
maybeLater Nothing = done
maybeLater (Just v) = schedLater v

affTask :: (String -> Aff Unit) -> JobT Aff Unit
affTask = mkAffTask (const done) Just

affTaskI :: (Int -> Aff Unit) -> JobT Aff Unit
affTaskI = mkAffTask (const done) fromString

affLaterTask :: (String -> Aff (Maybe Int)) -> JobT Aff Unit
affLaterTask = mkAffTask maybeLater Just

affLaterTaskI :: (Int -> Aff (Maybe Int)) -> JobT Aff Unit
affLaterTaskI = mkAffTask maybeLater fromString

main :: Effect Unit
main = launchAff_ $ runWorkerT launchAff_ periodicHost do
  addFunc "func1" $ affTask someFunc1
  addFunc "func2" $ affTask someFunc2
  addFunc "func3" $ affTaskI someFunc3
  addFunc "func4" $ affLaterTask someFunc4
  addFunc "func5" $ affLaterTaskI someFunc5

  work 10

第五次迭代

如果我们的数据不是从 name 来获取呢?

module Worker2 where

import Prelude

import Effect (Effect)
import Effect.Aff (Aff, delay, launchAff_)
import Effect.Aff.Class (liftAff)
import Data.Time.Duration (Milliseconds (..))
import Periodic.Worker (addFunc, done, name, runWorkerT, work, JobT, schedLater, workload)
import Data.Maybe (Maybe (..))
import Data.Int (fromString)

periodicHost :: {port :: Int, host :: String}
periodicHost = {port: 5000, host: "127.0.0.1"}

someFunc1 :: String -> Aff Unit
someFunc1 _ = delay (Milliseconds 1000.0)

someFunc2 :: String -> Aff Unit
someFunc2 _ = delay (Milliseconds 1000.0)

someFunc3 :: Int -> Aff Unit
someFunc3 _ = delay (Milliseconds 1000.0)

someFunc4 :: String -> Aff (Maybe Int)
someFunc4 _ = pure $ Just 10

someFunc5 :: Int -> Aff (Maybe Int)
someFunc5 _ = pure Nothing

mkAffTask :: forall a b. (b -> JobT Aff Unit) -> (String -> Maybe a) -> JobT Aff String -> (a -> Aff b) -> JobT Aff Unit
mkAffTask finish mk get f = do
  n <- mk <$> get
  case n of
    Nothing -> done
    Just n0 -> do
      r <- liftAff $ f n0
      finish r

maybeLater :: Maybe Int -> JobT Aff Unit
maybeLater Nothing = done
maybeLater (Just v) = schedLater v

affTask_ :: JobT Aff String -> (String -> Aff Unit) -> JobT Aff Unit
affTask_ = mkAffTask (const done) Just

affTask :: (String -> Aff Unit) -> JobT Aff Unit
affTask = affTask_ name

affTaskI_ :: JobT Aff String -> (Int -> Aff Unit) -> JobT Aff Unit
affTaskI_ = mkAffTask (const done) fromString

affTaskI :: (Int -> Aff Unit) -> JobT Aff Unit
affTaskI = affTaskI_ name

affLaterTask_ :: JobT Aff String -> (String -> Aff (Maybe Int)) -> JobT Aff Unit
affLaterTask_ = mkAffTask maybeLater Just

affLaterTask :: (String -> Aff (Maybe Int)) -> JobT Aff Unit
affLaterTask = affLaterTask_ name

affLaterTaskI_ :: JobT Aff String -> (Int -> Aff (Maybe Int)) -> JobT Aff Unit
affLaterTaskI_ = mkAffTask maybeLater fromString

affLaterTaskI :: (Int -> Aff (Maybe Int)) -> JobT Aff Unit
affLaterTaskI = affLaterTaskI_ name

main :: Effect Unit
main = launchAff_ $ runWorkerT launchAff_ periodicHost do
  addFunc "func1" $ affTask someFunc1
  addFunc "func2" $ affTask someFunc2
  addFunc "func3" $ affTaskI someFunc3
  addFunc "func4" $ affLaterTask someFunc4
  addFunc "func5" $ affLaterTaskI someFunc5
  addFunc "func6" $ affTask_ workload someFunc1
  addFunc "func7" $ affTaskI_ workload someFunc3
  addFunc "func8" $ affLaterTask_ workload someFunc4
  addFunc "func9" $ affLaterTaskI_ workload someFunc5

  work 10

总结

经过五次迭代我们构造出一个非常复杂的 mkAffTask 函数。

相关资源:

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • afinalAfinal是一个android的ioc,orm框架 https://github.com/yangf...
    wgl0419阅读 6,432评论 1 9
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,288评论 19 139
  • 增长根分区的方法:1、EC2 先建立一个快照。 建立快照时,要选择已有卷的信息。2、建立完快照后,在卷功能,新建立...
    上善若水_001阅读 529评论 0 0
  • 如果风也会说话,那这个世界上一定没有哑巴
    鹤小姐阅读 161评论 0 0
  • 自助烧烤
    线上学阅读 302评论 0 0