Blog: bugstack.cn

Precipitation, share, grow, let yourself and others can gain something! 😄

One, foreword

Pretty code is the same, disgusting programs get promotions and raises.

Almost every programmer knows or understands design patterns, but most people are used to writing code with a shuttle. No matter how much business logic there is in thousands of lines per class, this development can be reduced to three steps; Define properties, create methods, call the show, Done! It’s just a little development, rebuilding the crematorium.

Good code doesn’t just do what it already does, it also considers future extensions. Loose coupling in structural design, easy to read and expand, high cohesion in domain implementation, no external leakage of implementation details from external interference. And this is a bit like the decoration of the MVC room and DDD room in the home. You would not allow hundreds of thousands of houses to leak the wiring pipes naked outside, nor would you allow the toilet to be put in the kitchen and the stove to be installed in the bathroom.

“Who invented design patterns?” The concept of design pattern was first proposed by Christopher Alexander in his book Architectural Pattern Language. This book introduces the “language” of urban design, providing 253 models for describing the construction of towns, neighborhoods, houses, gardens, rooms and the west. The basic unit of this “language” is pattern. Later, four authors – Erich Gamma, John Friesides, Ralph Johnson, and Richard Helm – embraced the concept of patterns. In 1994, they published Design Patterns: The Foundation of Reusable Object-oriented Software, which applied the concept of design patterns to the field of program development.

There are a few people out there who haven’t read the books on design patterns and can still write great code. This is mainly due to the experience extracted from the years of programming process through the tempering of numerous projects and the continuous pursuit of programming. This lesson will ultimately be almost identical to the design pattern, which is highly cohesive, low coupled, scalable, and reusable. You’ve probably had the same experience learning the source code for a framework and finding that some of the design is the same as it was when you were developing.

“How can I not learn design patterns?” Money was spent and books were bought. The code is still lumpy! Design patterns are developed from years of experience. It’s like when I tell you how to ride a bike or how to drive a car, but if you haven’t run thousands of kilometers, all you can remember is the theory, and it’s still a panic!

So, this design patterns series begins with the idea of using design patterns to optimize code. In order to learn design patterns and incorporate them into their own. Of course, there is a lot of practice to be done here, and it must be a combination of people and cars, so that we can build more reasonable code based on design patterns.

Second, development environment

  1. JDK 1.8

  2. Idea + Maven

  3. Involving three projects, you can pay attention to the “public number” : Bugstack wormhole stack, reply to download the source code. You’ll get a connection open after listing number 18: ITstack-Demo-Design

    engineering describe
    itstack-demo-design-1-00 Scenario simulation project, used to provide three groups of different award distribution interface
    itstack-demo-design-1-01 Using a lump of code to implement business requirements is also the use of ifelse
    itstack-demo-design-1-02 Design patterns are optimized to adapt code to generate contrast for learning
    • 1-00, 1 represents the first design pattern, the factory method pattern
    • 1-00, 00 represent simulated scenarios
    • 1-01, 01 represents the first implementation scheme, followed by 02, 03 and so on

Ii. Introduction of factory methods and models

Factory method pattern, image from refactoringguru.cn
  • Factory method pattern, image from refactoringguru.cn

The factory pattern, also known as the factory method pattern, is a creative design pattern that provides a method to create an object in a parent class and allows subclasses to determine the type of object to instantiate.

This design pattern is also one of the most common patterns in Java development. Its main intention is to define an interface for creating objects and let subclasses decide which factory class to instantiate. The factory pattern delays the creation of the factory class to subclasses.

Simply put, to provide extensibility of the code structure, shielding the concrete implementation logic in each function class. It’s easier for the outside world to just know how to call, and it’s also a way to get rid of a lot of ifesLses. Of course, there may be some disadvantages, such as the need to implement a large number of classes, how to maintain, how to reduce development costs. However, these problems can be gradually reduced in the combination of subsequent design patterns.

Three, the simulation awards a variety of goods

Simulate the distribution of multiple goods, bugStack wormhole stack

In order to make the whole study case more close to the actual development, the business under the marketing scenario in the Internet is simulated here. Due to the complex, changeable, and AD hoc nature of the marketing scenario, the design needed to be deeper, or it would often face emergent CRUD operations that would clutter up the code structure and make it difficult to maintain.

In marketing scenarios, it’s common for a user to do something; Punch in, share, leave a message, invite registration and so on, get rebate points, and finally exchange goods with points, so as to promote life and attract new ones.

So here we simulate the issuance of various types of goods in point exchange. Suppose we have the following three types of commodity interfaces now;

The serial number type interface
1 coupons CouponResult sendCoupon(String uId, String couponNumber, String uuid)
2 Physical commodity Boolean deliverGoods(DeliverReq req)
3 Third party IQiyi exchange card void grantToken(String bindMobileNumber, String cardId)

The following information can be seen from the above interfaces:

  • The three interfaces return different types, including object type, Boolean type, and a null type.
  • Different input parameters, coupons need to be copied, exchange cards need to be card ID, physical goods need to be shipped location (contained in the object).
  • In addition, with the development of subsequent business, other types of goods may be added. Because all of your development needs will come as your business expands into the market.

Four, with a lump of code implementation

If you don’t care about extensibility and just want to satisfy the requirement as quickly as possible, you can just use the IFelse statement to determine the different interfaces to satisfy the requirement. Maybe this is also some beginners programming partners, commonly used way. Let’s first implement the business requirements in this way.

1. Engineering structure

itstack-demo-design-1-01
└ ─ ─ the SRC├ ─ ─ the main│ └ ─ ─ Java│ └ ─ ─ org. Itstack. Demo. The design│ ├ ─ ─ AwardReq. Java│ ├ ─ ─ AwardRes. Java│ └ ─ ─ PrizeController. Java└ ─ ─ the test└ ─ ─ Java└ ─ ─ org. Itstack. Demo. Design. The test└ ─ ─ ApiTest. JavaCopy the code
  • The engineering structure is very simple, an input objectAwardReq, an output parameter objectAwardRes, and an interface classPrizeController

2. Ifelse implementation requirements

public class PrizeController {

    private Logger logger = LoggerFactory.getLogger(PrizeController.class);

    public AwardRes awardToUser(AwardReq req) {
 String reqJson = JSON.toJSONString(req);  AwardRes awardRes = null;  try {  logger.info("Prize distribution begins {}. req:{}", req.getuId(), reqJson);  // According to different types of goods [1 coupon, 2 physical goods, 3 third-party exchange cards (iQiyi)]  if (req.getAwardType() == 1) {  CouponService couponService = new CouponService();  CouponResult couponResult = couponService.sendCoupon(req.getuId(), req.getAwardNumber(), req.getBizId());  if ("0000".equals(couponResult.getCode())) {  awardRes = new AwardRes("0000"."Release successful");  } else {  awardRes = new AwardRes("0001", couponResult.getInfo());  }  } else if (req.getAwardType() == 2) {  GoodsService goodsService = new GoodsService();  DeliverReq deliverReq = new DeliverReq();  deliverReq.setUserName(queryUserName(req.getuId()));  deliverReq.setUserPhone(queryUserPhoneNumber(req.getuId()));  deliverReq.setSku(req.getAwardNumber());  deliverReq.setOrderId(req.getBizId());  deliverReq.setConsigneeUserName(req.getExtMap().get("consigneeUserName"));  deliverReq.setConsigneeUserPhone(req.getExtMap().get("consigneeUserPhone"));  deliverReq.setConsigneeUserAddress(req.getExtMap().get("consigneeUserAddress"));  Boolean isSuccess = goodsService.deliverGoods(deliverReq);  if (isSuccess) {  awardRes = new AwardRes("0000"."Release successful");  } else {  awardRes = new AwardRes("0001"."Release failed");  }  } else if (req.getAwardType() == 3) {  String bindMobileNumber = queryUserPhoneNumber(req.getuId());  IQiYiCardService iQiYiCardService = new IQiYiCardService();  iQiYiCardService.grantToken(bindMobileNumber, req.getAwardNumber());  awardRes = new AwardRes("0000"."Release successful");  }  logger.info("Prize distribution completed {}.", req.getuId());  } catch (Exception e) {  logger.error("Prize distribution failed {}. req:{}", req.getuId(), reqJson, e);  awardRes = new AwardRes("0001", e.getMessage());  }   return awardRes;  }   private String queryUserName(String uId) {  return "Flower";  }   private String queryUserPhoneNumber(String uId) {  return "15200101232";  }  } Copy the code
  • Above is the use ofifelseVery direct implementation of the business requirements of a lump of code, if only from a business perspective, the r & D implementation of the function on schedule or even ahead of schedule.
  • This code is fine at the moment, but it will be a pain to take over after several iterations and expansions. The cost of reconstruction is high, and it needs to clarify the use of each previous interface. The test regression verification takes a long time, so it needs to verify all of them once. This is where a lot of people don’t want to take over someone else’s code, and then lose development time if they do. So imagine thisifelseIt will continue to increase.

3. Test and verify

Write a unit test to validate the interface approach written above, and getting into the habit of unit testing will improve your code quality.

Writing test classes:

@Test
public void test_awardToUser(a) {
    PrizeController prizeController = new PrizeController();
    System.out.println("\r\n Simulated coupon Issuing test \r\n");
    // Simulate coupon issuing test
 AwardReq req01 = new AwardReq();  req01.setuId("10001");  req01.setAwardType(1);  req01.setAwardNumber("EGM1023938910232121323432");  req01.setBizId("791098764902132");  AwardRes awardRes01 = prizeController.awardToUser(req01);  logger.info("Request parameters: {}", JSON.toJSON(req01));  logger.info("Test result: {}", JSON.toJSON(awardRes01));  System.out.println("\r\n Simulation method Physical goods \r\n");  // Simulation method physical goods  AwardReq req02 = new AwardReq();  req02.setuId("10001");  req02.setAwardType(2);  req02.setAwardNumber("9820198721311");  req02.setBizId("1023000020112221113");  Map<String,String> extMap = new HashMap<String,String>();  extMap.put("consigneeUserName"."Thank you airplane.");  extMap.put("consigneeUserPhone"."15200292123");  extMap.put("consigneeUserAddress"."Changchun city, Jilin Province. XX Street, Shuangyang District. Tan Xi Yuan District.#18-2109");  req02.setExtMap(extMap);   commodityService_2.sendCommodity("10001"."9820198721311"."1023000020112221113", extMap);   AwardRes awardRes02 = prizeController.awardToUser(req02);  logger.info("Request parameters: {}", JSON.toJSON(req02));  logger.info("Test result: {}", JSON.toJSON(awardRes02));  System.out.println("\r\n Third Party Redemption Card (IQiyi)\r\n");  AwardReq req03 = new AwardReq();  req03.setuId("10001");  req03.setAwardType(3);  req03.setAwardNumber("AQY1xjkUodl8LO975GdfrYUio");  AwardRes awardRes03 = prizeController.awardToUser(req03);  logger.info("Request parameters: {}", JSON.toJSON(req03));  logger.info("Test result: {}", JSON.toJSON(awardRes03)); } Copy the code

Results:

Mock coupon delivery test
22:17:55.668[the main] INFO O.I.D emo. Design. PrizeController issue start - the prize10001. req:{"awardNumber":"EGM1023938910232121323432"."awardType":1."bizId":"791098764902132"."uId":"10001"}
Simulate issuing a coupon:10001,EGM1023938910232121323432,791098764902132
22:17:55.671[the main] INFO O.I.D emo. Design. Issue complete PrizeController - the prize10001.22:17:55.673[the main] INFO org. Itstack. Demo. Test. The ApiTest - request parameters: {"uId":"10001"."bizId":"791098764902132"."awardNumber":"EGM1023938910232121323432"."awardType":1} 22:17:55.674[the main] INFO org. Itstack. Demo. Test. The ApiTest - test results: {"code":"0000"."info":"Release successful"}  Simulation methods Physical goods 22:17:55.675[the main] INFO O.I.D emo. Design. PrizeController issue start - the prize10001. req:{"awardNumber":"9820198721311"."awardType":2."bizId":"1023000020112221113"."extMap": {"consigneeUserName":"Thank you airplane."."consigneeUserPhone":"15200292123"."consigneeUserAddress":"Changchun city, Jilin Province. XX Street, Shuangyang District. Tan Xi Yuan District.#18-2109"},"uId":"10001"} Simulated delivery of a physical commodity: {"consigneeUserAddress":"Changchun city, Jilin Province. XX Street, Shuangyang District. Tan Xi Yuan District.#18-2109"."consigneeUserName":"Thank you airplane."."consigneeUserPhone":"15200292123"."orderId":"1023000020112221113"."sku":"9820198721311"."userName":"Flower"."userPhone":"15200101232"} 22:17:55.677[the main] INFO O.I.D emo. Design. Issue complete PrizeController - the prize10001.22:17:55.677[the main] INFO org. Itstack. Demo. Test. The ApiTest - request parameters: {"extMap": {"consigneeUserName":"Thank you airplane."."consigneeUserAddress":"Changchun city, Jilin Province. XX Street, Shuangyang District. Tan Xi Yuan District.#18-2109"."consigneeUserPhone":"15200292123"},"uId":"10001"."bizId":"1023000020112221113"."awardNumber":"9820198721311"."awardType":2} 22:17:55.677[the main] INFO org. Itstack. Demo. Test. The ApiTest - test results: {"code":"0000"."info":"Release successful"}  Third Party Redemption Card (IQiyi) 22:17:55.678[the main] INFO O.I.D emo. Design. PrizeController issue start - the prize10001. req:{"awardNumber":"AQY1xjkUodl8LO975GdfrYUio"."awardType":3."uId":"10001"} Issue a simulated iQiyi membership card:15200101232, AQY1xjkUodl8LO975GdfrYUio22:17:55.678[the main] INFO O.I.D emo. Design. Issue complete PrizeController - the prize10001.22:17:55.678[the main] INFO org. Itstack. Demo. Test. The ApiTest - request parameters: {"uId":"10001"."awardNumber":"AQY1xjkUodl8LO975GdfrYUio"."awardType":3} 22:17:55.678[the main] INFO org. Itstack. Demo. Test. The ApiTest - test results: {"code":"0000"."info":"Release successful"}  Process finished with exit code 0 Copy the code
  • The results are good, meet all current business product requirements, and write fast. But!!! It’s really hard to maintain!

Fifth, factory mode optimization code

The next step is to use the factory method pattern for code optimization, a minor “refactoring.” You’ll find that the structure of your code is clear and extensible for the next new business. However, it will be improved in actual use. At present, the core part is only extracted and reflected in front of you, which is convenient for learning.

1. Engineering structure

itstack-demo-design-1-02
└ ─ ─ the SRC├ ─ ─ the main│ └ ─ ─ Java│ └ ─ ─ org. Itstack. Demo. The design│ ├ ─ ─ store│ │ ├ ─ ─ impl│ │ │ ├ ─ ─ CardCommodityService. Java│ │ │ ├ ─ ─ CouponCommodityService. Java│ │ │ └ ─ ─ GoodsCommodityService. Java│ │ └ ─ ─ ICommodity. Java│ └ ─ ─ StoreFactory. Java└ ─ ─ the test└ ─ ─ Java└ ─ ─ org. Itstack. Demo. Design. The test└ ─ ─ ApiTest. JavaCopy the code
  • First, from the above engineering structure do you have some sense, such as; It looks clean, the layering is more extensible, and it seems possible to imagine what each class does.
  • It doesn’t matter if you can’t understand why you changed it this way. Because you’re learning the power of design patterns through articles like this. And then get the source code, the actual operation of a few times will slowly masterThe factory patternSkills.

2. Code implementation

2.1 Define the award awarding interface

public interface ICommodity {

    void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception;

}
Copy the code
  • All prizes, whether physical, virtual or third-party, need to be processed through our program to realize this interface, so as to ensure the unity of the final input and output.
  • The input parameters of an interface include;The user ID,The prize ID,Business IDAs well asExtension fieldUsed to process the delivery address of physical goods.

2.2 Realizing the Interface for Distributing prizes

coupons

public class CouponCommodityService implements ICommodity {

    private Logger logger = LoggerFactory.getLogger(CouponCommodityService.class);

    private CouponService couponService = new CouponService();
  public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {  CouponResult couponResult = couponService.sendCoupon(uId, commodityId, bizId);  logger.info("Request parameter [coupon] => uId: {} commodityId: {} bizId: {} extMap: {}", uId, commodityId, bizId, JSON.toJSON(extMap));  logger.info("Test Results [coupons] : {}", JSON.toJSON(couponResult));  if (!"0000".equals(couponResult.getCode())) throw new RuntimeException(couponResult.getInfo());  }  } Copy the code

Physical commodity

public class GoodsCommodityService implements ICommodity {

    private Logger logger = LoggerFactory.getLogger(GoodsCommodityService.class);

    private GoodsService goodsService = new GoodsService();
  public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {  DeliverReq deliverReq = new DeliverReq();  deliverReq.setUserName(queryUserName(uId));  deliverReq.setUserPhone(queryUserPhoneNumber(uId));  deliverReq.setSku(commodityId);  deliverReq.setOrderId(bizId);  deliverReq.setConsigneeUserName(extMap.get("consigneeUserName"));  deliverReq.setConsigneeUserPhone(extMap.get("consigneeUserPhone"));  deliverReq.setConsigneeUserAddress(extMap.get("consigneeUserAddress"));   Boolean isSuccess = goodsService.deliverGoods(deliverReq);   logger.info("Request parameter [coupon] => uId: {} commodityId: {} bizId: {} extMap: {}", uId, commodityId, bizId, JSON.toJSON(extMap));  logger.info("Test Results [coupons] : {}", isSuccess);   if(! isSuccess)throw new RuntimeException("Physical commodity delivery failed.");  }   private String queryUserName(String uId) {  return "Flower";  }   private String queryUserPhoneNumber(String uId) {  return "15200101232";  }  } Copy the code

Third party exchange card

public class CardCommodityService implements ICommodity {

    private Logger logger = LoggerFactory.getLogger(CardCommodityService.class);

    // Simulate injection
 private IQiYiCardService iQiYiCardService = new IQiYiCardService();   public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {  String mobile = queryUserMobile(uId);  iQiYiCardService.grantToken(mobile, bizId);  logger.info("Request parameter [iQiyi exchange card] => uId: {} commodityId: {} bizId: {} extMap: {}", uId, commodityId, bizId, JSON.toJSON(extMap));  logger.info("Test Result [IQiyi Exchange Card] : Success");  }   private String queryUserMobile(String uId) {  return "15200101232";  }  } Copy the code
  • It can be seen from the above that the implementation of each prize is included in its own class. The addition, modification or deletion will not affect the testing of other prize functions and reduce the possibility of regression testing.
  • Subsequent newly added prizes only need to be filled according to this structure, which is very easy to maintain and expand.
  • After unifying the entry and exit parameters, the caller no longer needs to care about the internal logic of the award distribution, and can handle it in a unified way.

2.3 Create shop factories

public class StoreFactory {

    public ICommodity getCommodityService(Integer commodityType) {
        if (null == commodityType) return null;
        if (1 == commodityType) return new CouponCommodityService();
 if (2 == commodityType) return new GoodsCommodityService();  if (3 == commodityType) return new CardCommodityService();  throw new RuntimeException("Non-existent types of goods and services.");  }  } Copy the code
  • Here we define a factory class for a store that implements services for various goods by type. Your code can be handled very cleanly, and the subsequent additions can be extended here. If you don’t like itifJudgment can also be usedswitchormapConfiguration structure makes code cleaner.
  • In addition, many code checks software and coding requirements, and do not like if statements without an extension, here to give you a cleaner representation of the logic. Parentheses can be added to the actual business code.

3. Test and verify

Writing test classes:

@Test
public void test_commodity(a) throws Exception {
    StoreFactory storeFactory = new StoreFactory();
    // 1. Coupons
    ICommodity commodityService_1 = storeFactory.getCommodityService(1);
 commodityService_1.sendCommodity("10001"."EGM1023938910232121323432"."791098764902132".null);  // 2  ICommodity commodityService_2 = storeFactory.getCommodityService(2);   Map<String,String> extMap = new HashMap<String,String>();  extMap.put("consigneeUserName"."Thank you airplane.");  extMap.put("consigneeUserPhone"."15200292123");  extMap.put("consigneeUserAddress"."Changchun city, Jilin Province. XX Street, Shuangyang District. Tan Xi Yuan District.#18-2109");   commodityService_2.sendCommodity("10001"."9820198721311"."1023000020112221113", extMap);  // 3. Third-party Exchange card (IQiyi)  ICommodity commodityService_3 = storeFactory.getCommodityService(3);  commodityService_3.sendCommodity("10001"."AQY1xjkUodl8LO975GdfrYUio".null.null); } Copy the code

Results:

Simulate issuing a coupon:10001,EGM1023938910232121323432,791098764902132
22:48:10.922[the main] INFO O.I.D.D.S.I.C ouponCommodityService - request parameter (coupons) = > uId:10001CommodityId: EGM1023938910232121323432 bizId:791098764902132ExtMap:null
22:48:10.957[the main] INFO O.I.D.D.S.I.C ouponCommodityService - test results (coupons) : {"code":"0000"."info":"Release successful"}
Simulated delivery of a physical commodity: {"consigneeUserAddress":"Changchun city, Jilin Province. XX Street, Shuangyang District. Tan Xi Yuan District.#18-2109"."consigneeUserName":"Thank you airplane."."consigneeUserPhone":"15200292123"."orderId":"1023000020112221113"."sku":"9820198721311"."userName":"Flower"."userPhone":"15200101232"}
22:48:10.962[the main] INFO O.I.D.D.S.I MPL. GoodsCommodityService - request parameter (coupons) = > uId:10001CommodityId:9820198721311BizId:1023000020112221113ExtMap: {"consigneeUserName":"Thank you airplane."."consigneeUserAddress":"Changchun city, Jilin Province. XX Street, Shuangyang District. Tan Xi Yuan District.#18-2109"."consigneeUserPhone":"15200292123"}
22:48:10.962[the main] INFO O.I.D.D.S.I MPL. GoodsCommodityService - test results (coupons) :true Issue a simulated iQiyi membership card:15200101232.null 22:48:10.963[the main] INFO O.I.D.D.S.I MPL. CardCommodityService - request parameters [iQIYI cash card] = > uId:10001CommodityId: AQY1xjkUodl8LO975GdfrYUio bizId:nullExtMap:null 22:48:10.963[the main] INFO O.I.D.D.S.I MPL. CardCommodityService - test results [iQIYI cash card] : success Process finished with exit code 0 Copy the code
  • The running results are normal, meeting both the requirements of the business product and the pursuit of the code. With code deployment up and running, there’s no panic, no midnight phone call.
  • In addition, it can be seen from the running test results that the integrity of a whole set of award distribution service can be clearly seen after encapsulation, which unified the input and results.

Six, summarized

  • The factory method pattern is not complicated in terms of top-down optimization, and even the development structure becomes simpler once you understand it.
  • Once the benefits of such development are known, the advantages can also be summarized;Avoid logical coupling of the creator to a specific product,To fulfill a single responsibility, each implementation of business logic is done in its own class,New product types can be introduced into a program without changing the use caller to satisfy the open close principle. However, there are some problems with this, such as having a very large number of award types, so the subclasses of implementation can expand very rapidly. Therefore, other patterns will also be used for optimization, which will be gradually covered in subsequent design patterns.
  • It’s often easier to look at design patterns from a case study than from a theory, because a case study is the best way to shorten the theory to a hands-on one. If you’re already learning something, be sure to try it.

Seven, past recommendation

  • Java Development Architecture: Introduction to domain-driven Design DDD Landing
  • Java Development Architecture: DDD Model Domain-level decision Tree Service Design
  • Java development architecture: domain-driven design architecture builds microservices based on SpringCloud
  • 110,000 words bytecode programming series collection broadcast

Eight, eggs

CodeGuide | programmers coding guidelines Go! This code base is a technical summary of the author’s learning process of Internet Java development for many years. It aims to provide you with a clear and detailed learning tutorial, focusing on the writing of Java core content. If the warehouse can help you, please give support (attention, like, share)!

CodeGuide | programmers coding guidelines