代码是如何变得抽象?

作者: Lupino | 来源:发表于2018-07-31 23:04 被阅读49次

    从简单的代码开始,慢慢的迭代,经过几次变得抽象复杂起来。
    本文将以定时任务系统 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 函数。

    相关资源:

    相关文章

      网友评论

        本文标题:代码是如何变得抽象?

        本文链接:https://www.haomeiwen.com/subject/btekvftx.html