基於TODO的開發方法

IvanEye發表於2018-03-25

之前買了一本書,叫《架構探險—從零開始寫Java Web框架 》(不推薦購買~),一本標題黨書籍!但是我很推崇作者寫程式碼的方式,就是基於TODO的方式進行開發!

個人認為以基於TODO的方式進行開發,至少有如下幾點優勢:

  • 有助於理解需求
  • 有助於梳理業務流程
  • 有助於任務拆解和程式碼封裝
  • TODO即註釋
  • 更易於進入心流體驗

同時還能避免如下兩種情況

v2-5677a79638f49199a4af13e24e7fe7b1_b.jpg | center | 410x657

image | center | 285x1073

下面我以Blog的建立流程為例,來演示基於TODO的開發方式,並說明為何基於TODO的開發方式有如上優勢!

後端的開發框架請見Web開發框架推導

流程演示

後端開發流程

基於上面的開發框架,整體流程就是Controller->Service->Mapper->Database! 就Blog建立流程來說,我們會有BlogController->BlogService->BlogMapper->Database的流程! 那我們的開發流程如下:

Step1:

@RestController
public class BlogController{
    //todo 建立blog流程
    //todo 接收引數
    //todo 驗證欄位
    //todo 構建Model
    //todo 委託BlogService
    //todo 返回blog主鍵
    //todo 處理可能異常    
}

@Service
public class BlogService{
    //todo 建立blog
    //todo 設定建立資訊
    //todo 委託blogMapper執行
    //todo 返回主鍵
}
複製程式碼

Step2:

@RestController
public class BlogController{
    //建立blog流程
    @PostMapping("/blog")
    public Long create(BlogDto blog){
        //todo 接收引數
        //todo 驗證欄位
        //todo 構建Model
        //todo 委託BlogService
        //todo 返回blog主鍵
        //todo 處理可能異常    
        return null;
    }
}
複製程式碼

Step3:

@RestController
public class BlogController{
    //建立blog流程
    //接收引數
    @PostMapping("/blog")
    public Long create(@RequestBody @Validated BlogDto blog, BindingResult result){
        //驗證欄位
        if (bindResult.hasErrors()) {
            throw new BindingException(bindResult.getFieldError().getDefaultMessage());
        }
        //todo 構建Model
        //todo 委託BlogService
        //todo 返回blog主鍵
        //todo 處理可能異常    
        return null;
    }
}
複製程式碼

Step4:

@RestController
public class BlogController{
    //建立blog流程
    //接收引數
    @PostMapping("/blog")
    public ResponseEntity create(@RequestBody @Validated BlogDto blogDto, BindingResult result){
        //驗證欄位
        if (bindResult.hasErrors()) {
            throw new BindingException(bindResult.getFieldError().getDefaultMessage());
        }
        //構建Model
        Blog blog = BeanUtils.copyProperties(blogDto,Blog.class);
        //todo 委託BlogService
        //todo 返回blog主鍵
        //todo 處理可能異常    
        return ResponseEntity.ok("");
    }
}
複製程式碼

Step5:

@RestController
public class BlogController{

    @Autowired
    private BlogService blogService;

    //建立blog流程
    //接收引數
    @PostMapping("/blog")
    public ResponseEntity create(@RequestBody @Validated BlogDto blogDto, BindingResult result){
        //驗證欄位
        if (bindResult.hasErrors()) {
            throw new BindingException(bindResult.getFieldError().getDefaultMessage());
        }
        //構建Model
        Blog blog = BeanUtils.copyProperties(blogDto,Blog.class);
        //委託BlogService
        Long recId = blogService.create(blog);
        //todo 返回blog主鍵
        //todo 處理可能異常    
        return ResponseEntity.ok("");
    }
}

@Service
public class BlogService{
    //建立blog
    public Long create(Blog blog){
        //todo 設定建立資訊
        //todo 委託blogMapper執行
        //todo 返回主鍵
        return null;
    }
}
複製程式碼

Step6:

@RestController
public class BlogController{

    @Autowired
    private BlogService blogService;

    //建立blog流程
    //接收引數
    @PostMapping("/blog")
    public ResponseEntity create(@RequestBody @Validated BlogDto blogDto, BindingResult result){
        //驗證欄位
        if (bindResult.hasErrors()) {
            throw new BindingException(bindResult.getFieldError().getDefaultMessage());
        }
        //構建Model
        Blog blog = BeanUtils.copyProperties(blogDto,Blog.class);
        //委託BlogService
        Long recId = blogService.create(blog);
        //返回blog主鍵
        return ResponseEntity.ok(recId);
        //todo 處理可能異常    
    }
}
複製程式碼

Step7:

@RestController
public class BlogController{

    @Autowired
    private BlogService blogService;

    //建立blog流程
    //接收引數
    @PostMapping("/blog")
    public ResponseEntity create(@RequestBody @Validated BlogDto blogDto, BindingResult result){
        try{
            //驗證欄位
            if (bindResult.hasErrors()) {
                throw new BindingException(bindResult.getFieldError().getDefaultMessage());
            }
            //構建Model
            Blog blog = BeanUtils.copyProperties(blogDto,Blog.class);
            //委託BlogService
            Long recId = blogService.create(blog);
            //返回blog主鍵
            return ResponseEntity.ok(recId);
        }catch (BusinessException e) {
            //處理可能異常  
            logger.error("Create Blog Error!", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("Create Blog Error!" + e.getMessage());
        } catch (Exception e) {
            logger.error("Create Blog Error!", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("Create Blog Error!" + e.getMessage());
        }
    }
}
複製程式碼

Step8:

@Service
public class BlogService{
    //建立blog
    public Long create(Blog blog){
        //設定建立資訊
        Long userId = UserContext.getUser().getUserId();
        blog.setCreateId(userId);
        blog.setUpdateId(userId);
        blog.setCreateTime(new Date());
        blog.setUpdateTime(new Date());
        //todo 委託blogMapper執行
        //todo 返回主鍵
        return null;
    }
}
複製程式碼

Step9:

@Service
public class BlogService{

    @Autowired
    private BlogMapper blogMapper;

    //建立blog
    public Long create(Blog blog){
        //設定建立資訊
        Long userId = UserContext.getUser().getUserId();
        blog.setCreateId(userId);
        blog.setUpdateId(userId);
        blog.setCreateTime(new Date());
        blog.setUpdateTime(new Date());
        //委託blogMapper執行
        blogMapper.insert(blog);
        //todo 返回主鍵
        return null;
    }
}
複製程式碼

Step10:

@Service
public class BlogService{

    @Autowired
    private BlogMapper blogMapper;

    //建立blog
    public Long create(Blog blog){
        //設定建立資訊
        Long userId = UserContext.getUser().getUserId();
        blog.setCreateId(userId);
        blog.setUpdateId(userId);
        blog.setCreateTime(new Date());
        blog.setUpdateTime(new Date());
        //委託blogMapper執行
        blogMapper.insert(blog)
        //返回主鍵
        return blog.getRecId();
    }
}
複製程式碼

前端開發流程

前端的開發除了需要處理程式碼邏輯,還需要處理頁面流程!依然可以以TODO的方式來處理,藉助於a標籤和按鈕,可以把頁面流給串起來! 接著上面的Blog的CRUD邏輯,這裡僅列出示例,不再演示流程,開發流程和上面的流程一致!

list.html:

<!-- todo 新增Blog-->
<a href="new.html">新增</a>

<!--TODO 搜尋-->
<!--TODO 列表顯示,操作-->
<table>
    <tr>
        <td>
            <a href="view.html">檢視</a>
            <a href="edit.html">編輯</a>
            <a href="delete.html">刪除</a>
        </td>
    </tr>
</table>
<!--TODO 翻頁-->
複製程式碼

new.html:

<!--todo  表單欄位-->
<!--todo  驗證規則-->
<!--todo 儲存邏輯-->
<a href="list.html">儲存按鈕</a>
<!--todo 取消邏輯-->
<a href="list.html">取消按鈕</a>
複製程式碼

view.html:

<!--todo  展示欄位-->
<!--todo 返回邏輯,按場景返回?-->
<a href="list.html">返回按鈕</a>
複製程式碼

edit.html:

<!--todo  表單欄位-->
<!--todo  欄位賦值-->
<!--todo  驗證規則-->
<!--todo 儲存邏輯-->
<a href="list.html">儲存按鈕</a>
<!--todo 取消邏輯-->
<a href="list.html">取消按鈕</a>
複製程式碼

優勢

首先問一個問題,對於你接收到的資訊,你是以什麼樣的標準來評判你理解了或學會了?就是__用你自己的話再說一遍__! 基於TODO的開發方法就是以此為基礎:

  • 首先基於需求,以TODO文字的形式將業務流程寫下來。寫下來以後,可以和需求去確認,修正偏差,既__有助於理解需求__也__有助於梳理業務流程__
  • 同時,可以看出,每個TODO的工作量都比較小,實際上也起到了__任務拆解和程式碼封裝__的作用。既然任務拆解了,編寫的程式碼也就相應的被拆解為一個個的功能程式碼了。當然對於邏輯較複雜的程式碼,還是需要使用重構手段,來進一步的對程式碼進行封裝
  • 程式碼編寫完後,不需要將TODO完全刪除,只需要把TODO字樣去除,__TODO就變成了註釋__了!
  • 由於每個TODO的實現時間都較短(主要看拆解得如何),你開發的每個小功能,實際上都是在解決一個TODO,就像遊戲裡的打怪一樣,能得到__準即時反饋__,心理得到滿足!這是__進入心流體驗__的一個必要條件!

關於心流體驗

  • 心流英文叫"flow",我第一次見到這個詞是在《人件》這本書上!這是兩年前寫的讀書筆記《我的管理實踐---《人件》讀後感》!
  • 心流的解釋有很多,有興趣的可以去搜尋一下
  • 相信很多人都經歷過,比如你做某件事時很專注(寫程式碼、玩遊戲等等),當做完後,你以為沒多長時間,但是回過神來一看,好幾個小時已經過去了!

寫在最後

本文只是演示了一種個人比較推崇的寫程式碼的方式,並解釋了為什麼推崇這種方式!當然,僅供參考!畢竟 適合自己的才是最好的


公眾號:ivaneye

相關文章