Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Growing Object-Oriented System

Kent Wang
December 10, 2012

Growing Object-Oriented System

Kent Wang

December 10, 2012
Tweet

Other Decks in Programming

Transcript

  1. Feedback is Critical Understand the Problem (Analysis) Broad-Brush Design (Architecture)

    Test Driven Development (Implementation) Deployable System
  2. Understand The Problem • User Story • Use Case •

    Conceptual Model • Color Modeling
  3. User Story Changed Users could pay for their orders from

    various platform via tenpay or alipay
  4. The Missing Concept Payment Partner User Payment Type Buyer Seller

    Partner Trade Provider Payment Payment Provider
  5. Walking Through Scenarios User Submit Payment Save Payment Get Payment

    Parameter Submit To Provider Presentation Layer Application Layer
  6. Integrity Problem ChargePlan ChargePlan LineItem ChargePlan LineItem amount = 50

    amount = 50 amount = 100 ChargePlan LineItem amount = 50
  7. Design To Test Tenpay Service Facade Mock Tenpay Relay Client

    Tenpay Service Facade Test 1. RegisterChargePlan(plan) 3. Execute(request, response) 4. Checking Request 5. Return Response 6. Return Response 7. Parse Response 2. Assemble Request 8. Return Result 9. Checking Result
  8. Implementation • Design Tools • Make A New Type •

    Consistent Abstraction Level • Open Close Principle • Extract Hidden Concept
  9. CRC Card CTenpayProviderType Make Tenpay Trade No Make Tenpay Settle

    No Get Request Builder CTenpayTradeNo CTenpayProvider CTenpayRequest Builder
  10. CRC Card CTenpayProviderType Make Tenpay Trade No Make Tenpay Settle

    No Get Request Builder CTenpayTradeNo CTenpayProvider CTenpayRequest Builder Class Responsibility Collaborator
  11. Duplicated Code // inside NotifyPayment function // inside GetPayment fuction

    if (sProviderTradeNo.length() != 28) { return ERR_APP_PARAM_INVALID; } uint64_t ddwProviderPartnerNo = lexical_cast<uint64_t>(sProviderTradeNo.substr(0, 10)) uint64_t ddwSerialNo = lexical_cast<uint64_t>(sProviderTradeNo.substr(18)); dwRet = GetPaymentByProviderTradeNo( ddwProviderPartnerNo, ddwSerialNo);
  12. Make A New Type CTenpayTradeNo Make Tenpay Trade No Parse

    Tenpay Trade No Verify Tenpay Trade No CTenpayProvider Get Date From Trade No Get Serial No From Trade No
  13. Use The New Type // inside NotifyPayment function // inside

    GetPayment fuction CTenpayTradeNo oTradeNo; if (!CProviderTradeNo::TryParse(sProviderTradeNo, oTradeNo) { return ERR_APP_PARAM_INVALID; } dwRet = GetPaymentByProviderTradeNo( oTradeNo.GetProviderPartnerNo(), oTradeNo.GetProviderTradeSerialNo());
  14. Too Much Detail // inside SetupPayment function if (dwRet ==

    ERR_PAYMENT_NOT_FOUND) { CPayment oPayment; oPayment.SetPaymentId(NextPaymentId()); oPayment.SetAmount(rTrade.GetAmount()); oPayment.SetBuyerUid(rTrade.GetBuyerUid()); oPayment.SetSellerUid(rTrade.GetSellerUid()); // ... 50 lines more oPayment.SetLastUpdateTime(CTime::Now()); dwRet = m_pDao->SavePayment(oPayment); if (dwRet != 0) { // ... 10 lines of error handling } return dwRet; }
  15. Too Much Detail // inside SetupPayment function if (dwRet ==

    ERR_PAYMENT_NOT_FOUND) { CPayment oPayment; oPayment.SetPaymentId(NextPaymentId()); oPayment.SetAmount(rTrade.GetAmount()); oPayment.SetBuyerUid(rTrade.GetBuyerUid()); oPayment.SetSellerUid(rTrade.GetSellerUid()); // ... 50 lines more oPayment.SetLastUpdateTime(CTime::Now()); dwRet = m_pDao->SavePayment(oPayment); if (dwRet != 0) { // ... 10 lines of error handling } return dwRet; }
  16. Code Closer To Problem // inside SetupPayment function if (IsPaymentNotExist(dwRet))

    { return CreatePayment(); } else { return MakeSurePriceHasNotChanged(); }
  17. Code Closer To Problem // inside SetupPayment function if (IsPaymentNotExist(dwRet))

    { return CreatePayment(); } else { return MakeSurePriceHasNotChanged(); }
  18. Feature Envy // inside GetPaymentParameter function ... if (rProvider.GetType() ==

    TENPAY) { BuildBasicParameter(rPayment); if (rSpec.GetPaymentMethod() == STANDARD) { BuildParameterOfTenpayStandardPayment(rPayment); } else if (rSpec.GetPaymentMethod() == BANK) { BuildParameterOfTenpayBankPayment(rPayment); } } ...
  19. Feature Envy // inside GetPaymentParameter function ... if (rProvider.GetType() ==

    TENPAY) { BuildBasicParameter(rPayment); if (rSpec.GetPaymentMethod() == STANDARD) { BuildParameterOfTenpayStandardPayment(rPayment); } else if (rSpec.GetPaymentMethod() == BANK) { BuildParameterOfTenpayBankPayment(rPayment); } } ...
  20. Power Of Values IPaymentRequestBuilder Build Payment Request GetPaymentUrl CopyPaymentParameters CProviderType

    CProvider CTenpayStandardPayment RequestBuilder Build Payment Request GetPaymentUrl CopyPaymentParameters CTenpayProviderType CTenpayBankPayment RequestBuilder Build Payment Request GetPaymentUrl CopyPaymentParameters CTenpayProviderType
  21. The Clean Way // inside GetPaymentParameter function ... IPaymentRequestBuilder* pBuilder

    = rSpec.GetPaymentMethod(). NewPaymentRequestBuilder(rProvider); pBuilder->Build(rPayment); ...
  22. Extract Hidden Concept // inside NotifyPayment function ... if (rPayment.GetCurrentProviderPaymentId()

    == rProviderPayment.GetProviderPaymentId() && rPayment.IsWaitPay()) { rPayment.Accept(rProviderPayment); } ...
  23. Extract Hidden Concept // inside NotifyPayment function ... if (rPayment.GetCurrentProviderPaymentId()

    == rProviderPayment.GetProviderPaymentId() && rPayment.IsWaitPay()) { rPayment.Accept(rProviderPayment); } ...
  24. Extract Hidden Concept // inside NotifyPayment function IPaymentAcceptingPolicy* paymentAcceptingPolicy; ...

    if (paymentAcceptingPolicy->IsAllowedToAccept( rPayment, rProviderPayment)) { rPayment.Accept(rProviderPayment); } ...
  25. Extract Hidden Concept // inside NotifyPayment function IPaymentAcceptingPolicy* paymentAcceptingPolicy; ...

    if (paymentAcceptingPolicy->IsAllowedToAccept( rPayment, rProviderPayment)) { rPayment.Accept(rProviderPayment); } ...