Dive into abstraction and dynamic modeling

Teacher Xiao Peng, author of ZenUML.com, independent consultant, serving leading banks and retail companies in Australia, former ThoughtWorks China Continuous Delivery Practice Lead, translator of volumes 4 and 5 of "Model-Oriented Software Architecture".

The following is the video sharing draft of Mr. Xiao Peng's " Taking You In-depth Understanding of Abstraction and the Application of Abstraction in Software Design " .


What is Dynamic Modeling

Difference Between Static Modeling and Dynamic Modeling

Let's talk about dynamic modeling, which corresponds to static modeling. You can understand the differences in several concepts by comparing the two.

The concepts that the static model focuses on are static: class (Class), attribute (Attribute), method (Method), class relationship (Class relationship), class responsibility (Responsibility), which is to describe a static class in the language of the class. For example, in the understanding of birds, the static model is concerned with birds (classes), which attributes (eyes, mouths, wings), which methods (flying, sleeping), which class relationships (inherited from animal classes), and which Duties (sleep, tweet, fly).

The concept of the dynamic model is dynamic: object (Object), state (State), interaction (Interactions), object relationship (Object relationship), business logic (Business Logic), is to use the language of the object to describe a dynamic object . For example, with the understanding of sparrow objects, the dynamic model focuses on sparrows (objects), which states are contained (whether the eyes are closed), which interactions between objects are contained (whether the behavior of sparrows and woodpeckers is implemented in a differentiated form), and which object relationships are contained (This sparrow is the mother of another sparrow), which business logic is included (whether the sparrow needs to close its eyes when sleeping).

f7eb90139e0ccb8ae0219aca042f7283.png

Why Dynamic Modeling is Needed

In the field of object-oriented, there are a lot of books and articles on static modeling. "Design Patterns" is one of the most classic books, and I personally think that the strategy pattern is one of the most classic patterns. Its concept can be understood in this way. There is a strategy corresponding to an abstract class or an interface, and then this strategy has several specific subclasses. When we inject this strategy in the context, we can choose a specific class to execute at runtime .

The strategy pattern seems to be good, because it conforms to the principle of opening and closing, decoupling the concrete class and implementation. But if you don't have enough experience, you will find it very difficult to combine it with specific work, because when you really implement this model, there are still many questions to be answered:

First, how is the strategy constructed? Is it new at runtime, or created when the program starts, or created when a certain class needs it.

Second, how is the strategy injected? Is it injected in the constructor, or injected with a setter, or injected with a container.

Third, how is the strategy implemented? Or how to choose a strategy, whether to use if else, or according to pattern matching and so on.

These topics are actually the problems that we must think about and solve when we actually write code, and it is also the reason why we need to study dynamic object-oriented modeling.

Why is dynamic modeling not popular

I guess you see relatively few concepts about dynamic object-oriented modeling at work or in books. I think there may be two reasons.

First, simple examples cannot reflect the value of tools or methodologies. For example, to design a hello world, you have no way to say what strategy to use, what design pattern to implement it, and how to design its life cycle, etc. , these things have no practical application value.

The second point is that complex examples cannot explain the context in a short time, or require certain prior knowledge. If you use complex examples to tell, the time to introduce the context may be longer than the time to talk about tools or methodologies, which may limit his dissemination.

However, even if there are few books or articles, there are still people who study it. If you use dynamic object oriented programming to search, there are still some papers that will talk about this.

485fb8fe783d893c16b401df63bdfdb5.png

Related Research on Dynamic Modeling

I personally think that there are some researches related to the field of dynamic modeling, such as test-driven design/development, refactoring and specification by example.

The first one, Test Driven Design/Development. In fact, it takes a part from a large program, so that the classes and methods we want to test can be verified in a runtime environment. In fact, a small context is isolated from all codes for runtime code logic. verify.

Second, refactoring. It is related to both dynamic and static. It not only cares about the static part, such as how to extract classes and interfaces, but also cares about the dynamic part. For example, method calls, how to turn if-else into polymorphism, and so on.

The third one is specification by example. I remember that when I was in China, I did relevant training in some companies. I don’t know if anyone is still using this thing. I personally like this method very much, but I have never seen a company that uses this method abroad. .

Today we will use a very simplified version of the specification by example as an example to discuss a process of dynamic object-oriented modeling.

Dynamic modeling case

Statement of needs

Design a system for a tea shop in Wuyishan, which can calculate the total price of tea purchased by customers (the chart corresponds to the rules of related expenses)

630b44ee08ea448bdf7e21306f81762d.png

vs static modeling

Static modeling also has some methodological guidance. These are some methods taught by my teacher when I was in school, such as the dictionary method, that is, you first read a description of a requirement use case, and put forward the nouns in this description as the class name of the class . Propose the verb in the description as a method of the class. The object in the description is raised as a parameter of the method. The subject, predicate and object of this tea shop system case are quite complete.

You can also try it yourself. If you adopt the method of static modeling, imagine how many classes you will create? What methods does each class have? Attributes? The following are a few classes I tried to design with static modeling ideas: order class (Order), address class (Address), customer class (Customer), price class (Price), shipping class (ShippingFee), total price calculation class ( PriceCalculator).

However, there are often some problems in the design results of this method. For example, the code designed by static modeling may not conform to the SOLID principle, and there may be no way to answer the question of how to build, how to inject, and how to execute.

Next, let us try to design the tea shop system by means of dynamic modeling, and you can compare the differences in the design process.

What is one benefit of doing dynamic design? Especially when you have tools to assist the design, you can adjust your design very flexibly. I think this is very important. It is a bit similar to the difference between agile and waterfall. You don’t need to design a very OK, perfect design, but constant iteration and refactoring, and in the process, the design gets better.

Step 1: Design the Outermost Interface

In the example of calculating the total price of tea purchased, we assume that the most important thing is to design a total price calculation class (TotalPriceCal). This class provides a method to calculate the total price of a specified order. When the external system calls this method, it will return a price, which is our outermost interface.

8dbbae56557e5d6ecd5af289a48dd030.png

Step Two: Try to Implement

I suggest that everyone write this code with me. In the process of writing this code, you may understand why we call it dynamic modeling.

According to the conditions in the example, we need to pass in an address parameter (adress) and an initial price (price) in the TotalPriceCal.cal() method. According to the rules in the chart, we use the Zen tool to write pseudocode to implement the corresponding logic:

If the address is Jiangsu, Zhejiang and Shanghai (JZH), and the price is greater than or equal to 100, the shipping fee will be 0 and the total price will remain unchanged.

If the address is Jiangsu, Zhejiang and Shanghai (JZH), and the price is less than 100, we need to calculate a shipping fee, which is the price (price) multiplied by (multiply) shipping standard 0.3. Here we introduce a class called shippingFee, and then add this value with the total price (totalPrice).

If the country of the address is China (CN), we have to calculate the extra price according to 3%, because we use the else here, so we don’t need to consider Jiangsu, Zhejiang and Shanghai.

If the country of the address is Australia (AU), a tax (gst) must be introduced, the total price plus tax, and then the total price plus shipping.

Logic for other international locales, and so on. In the end, we designed three classes, which is our first pass design.

96763ddea1800335e237d14c94d5c38a.png

9f23e9e0e8fc0504d89ef9256966b487.png

Step 3: Refactor the implementation

Looking at the pseudocode on the left or the sequence diagram on the right, everyone should be able to see some bad smells: there are many repeated if-else, at this time we need to use some refactoring knowledge to optimize, we need to put The repeated parts in the code logic are extracted and refactored.

How to make different logic common? We can think that for each if branch, they have a logic for calculating tax (gstFee) and shipping fee (shippingFee), but some local tax (gstFee) is 0, and some are calculated according to the address. Similarly, In some places, the shipping fee (shippingFee) is 0, and in some places, the shipping fee needs to be calculated according to the address and order.

Accordingly, we can introduce a tax calculator (gstFeeCal) and a shipping calculator (shippingFeeCal), and we can use these two calculators to complete the logic for each if-else part: where there is no tax calculation , add the logic of tax calculation, gst=GstCal.get(add, price), similarly, if there is no part of calculating the freight, add the logic of freight calculation, ShippingFeeCal.get(add, price).

71482b919016cc2f180c83387acd9b29.png

What is the purpose of doing this? We can see that the refactored code has exactly the same processing logic for each if branch, and then we can remove these repeated codes (applause should be given here).

dc5c73e85f104c1791758292b6fd32f6.png

It can be seen that after we remove the repetitive logic through dynamic object-oriented modeling and the idea of ​​refactoring, our design immediately becomes very clear.

We introduce two calculators (calculator), a tax calculator (gstCalculator), and a shipping calculator (shippingCalculator), which are used to calculate tax and shipping respectively, and add it to the total price (totalPrice). Then you may say that this is just hiding the logic to the other side. Is such a design really good?

This is a very good question. Next, let's implement the specific logic of calculating shipping costs, that is, the code ShippingCalculator.get(add, price). The input parameters are address (add) and price (price). For the implementation method, please refer to Chapter 1. One-pass design process.

2f4f2353094d34d52119abf64551eaf9.png

Let's analyze the implementation of ShippingCalculator.get(add, price). If you want to expand the logic of shipping, for example, add Beijing-Tianjin-Hebei (JJH), we only need to change one place and add an if(address == JJH && price ...) { rate.set(xxx); } will do. Comply with the principle of opening and closing.

7c597136242987091519aaa9e15a1f19.png

OK, after this step is done, let's look back and forth at the design of this pseudocode (of course, you can also look at the sequence diagram). Do you feel that this picture actually has repetitions, that is, the logic of the two places in the red circle. They all get a fee, and then add it to the total price (totalPrice).

This means that we can abstract a thing called price calculator (priceCalculator), so that we actually generalize the two cases of gstCalculator and shippingFeeCalculator, which means that we have eliminated another duplicate code ( There should be applause here too).

What does the final implementation of this code represent? It looks like a strategy pattern. In our case, both strategies are used to calculate the price.

3f8d560cf57017588196a2c1035d97b0.png

The above discussion is the part of dynamic modeling, that is, how to build it, how to inject it, and how to execute it.

The first one is the construction of the object. In this example, we build directly new instances without using IOC containers.

The second one is object injection. In this example, we implement injection by creating instances of gstCal, shippingFeeCal, covidTxCal, etc., and adding them to the construction method of the total price calculator (TotalPriceCal).

The third is the execution of the object. In this example, we use the form of for-each to execute several calculators in sequence.

Finally, we can validate our design with SOLID design principles.

The first is single responsibility. The tax (gst) is done in the tax calculator (gstCalculator), and the adjustment of the tax will not affect the logic of the shipping fee calculator (shippingFeeCalculator), which satisfies a single responsibility.

Then the second principle of opening and closing, if we want to add a new calculator, for example, we need to add a special epidemic cost calculator (covidTaxCal) because of the epidemic. As long as it also implements the calculator interface, it can be added to the calculator collection (cals). In this way, you can basically think that it conforms to the principle of opening and closing. It is open for this extension and closed for modification (you don't need to change this method).

We will not talk about other design principles one by one, you can use these object-oriented design principles to verify it again.

Step 4: Object Lifecycle

At this point, we can clearly know the life cycle of the object through the sequence diagram after dynamic modeling: when the object is constructed, injected and executed.

The implementation of the following figure is actually not exactly the same as the implementation of the code in the first three steps. This is a very interesting point. In the implementation of the first three steps, various calculators are actually injected through the constructor, and this diagram is to create an instance in the method to realize the injection.

In the current context, it is actually difficult to say which is good and which is bad. In the end, we just use this dynamic modeling method to display the logic of the object at a lower cost. It would be easier and more convenient for everyone to communicate based on these diagrams, and you don’t have to wait until all its logic is implemented into code before you can communicate.

25085ebcff15144e7f001dd8bfd18447.png

Related Articles and Tools

B station video search: Take you to understand abstraction and the application of abstraction in software design

Related research: https://www.tutorialspoint.com/object_oriented_analysis_design/ooad_dynamic_modeling.htm

ZenUml source code: https://github.com/ZenUml

Q & A session

1. Where is the dynamic reflected?

questioner:

My biggest problem is that I don't understand how to "dynamically". I narrowed down the question a little bit to pave the way for the scene. I think the final effect of the case shared today is to pass a parent calculator, and then pass N child calculators into it . I think the only possible dynamic scenario is to provide the child calculator. Is this the understanding?

Answered by:

Going back to the part we just shared about why we need dynamic modeling, I think there are at least three parts that can reflect this dynamic: how to build, how to inject, and how to execute.

That is to say why I think dynamic modeling is important, I think these concepts are really not reflected in static modeling. Regarding dynamic modeling, I am talking about static modeling. In fact, you can look at many things in our industry from different angles, and they also have similarities, such as other modeling methodologies. Dynamic modeling will be involved, so similar concepts will exist in the methodology.

questioner:

Does this dynamic mean modifying some things in the structure so that the details don't need to be changed, and then change its behavior?

Answered by:

Let's look at this picture to understand that various object-oriented books basically contain these words: class, method, attribute, relationship between classes and classes, and class responsibilities. We rarely see these concepts of dynamic models: objects, state, interactions, object relations, business logic.

09ee73610ea6d92e3f5c06602d328499.png

Let's find our common place first, for example, you open the book Design Patterns. He will talk to you. I'm going to have a strategy class that has several subclasses. Then this strategy class is used as a field by this context class. The strategy pattern is about such a thing, do you agree?

questioner:

agree.

Answered by:

In fact, I also have a starting point for dynamic modeling. You can read the book I translated this pattern-oriented software architecture. Its fifth volume is called pattern language (pattern language). What problem does he want to solve? The author said that we already have so many design patterns. We call these design patterns words. The program we want to write is an article, the most basic unit of this article, if it wants to express a meaning, it must have at least one sentence.

A sentence in our natural language has a subject, a predicate, and an object. Among these 23 design patterns, which ones are in the position of the subject, which ones are in the position of the predicate, and which ones are in the position of the object, and what kind of combination can tell a story?

Let me give you an example of the simplest object-oriented pattern language. First, I want to create an object. We can create this object with the simplest creational design pattern—the singleton pattern. Then I want to use the strategy pattern to assemble his behavior, and then use a selector to match a strategy (the pattern of the selector is not in the 23 design patterns, but it is also a design pattern), and then execute it, thus forming a sentence.

But before this sentence appeared in the fifth volume of object design patterns (pattern language), the concept of pattern language was not abstracted in this field. Dynamic modeling is actually very close to pattern language. Pattern-oriented software architecture fifth Volume, this book is my thinking about this issue and entering a more systematic period. When I was reading and translating this book, I realized that what is missing in object-oriented analysis is this part.

I don't know if that answered your question.

questioner:

Let's say a sentence: I ate an apple, how can I use dynamic modeling to understand it.

Answered by:

Apple is like the strategy pattern. You told me that I had an apple, no matter how beautiful you described it to me, I would not eat it. I don't know if I'm going to eat it, or if I'm going to take the seeds out and plant it, or slice it up to see the cell structure inside. You need to put it into a context and make it clear how you want to use this apple, whether to eat it or plant a tree? At this time, you have to consider the dynamic model. If you do not consider the dynamic model, you cannot answer the three questions that the static model misses.

When I read the book Design Patterns more than ten years ago, I was also fascinated by it. But back to the project, I just don't know how to use it. For example, where is the familiar MVC, C created? Is C creating M, or C creating V, what is the relationship between M and V? Through this example, you will find that so many books on MVC have not clearly told you who created who.

At this time, you need a pattern language to tell. Of course, there are N kinds of ways to tell a story. You may create M with C, or C with M, but the process and selection of this creation reflect the two dynamics. Character.

questioner:

If you don't talk about too abstract things like Apple. That is to say, the creation of this piece, if I use this framework similar to spring framework (a complete IOC container) to create things, then the creation step has already been solved.

Answered by:

This is a very good question, that is, you realized that the original creation still happened, but it was hidden from you. As a rookie or novice, it is okay to understand this problem, because it is too difficult to create this thing, and it is too difficult to choose the right time to create an object. The emergence of tools or frameworks is to solve this problem. , the framework directly creates the object, and the programmer can just take it out and use it.

However, as an experienced programmer, you must understand how he created it, because although it is good to be hidden, you also need to know that being hidden is also risky, if you have worked for five or more than ten years , you must have encountered a problem: "I lost it, why did you hide this thing? I need a way to modify it!". So, it's not that the problem of creating objects doesn't exist anymore, it still exists, and it's a very important thing, I think we need to understand this process.

Going further, it is the problem of injection, so do I need to automatically inject all objects, taking spring as an example, can all objects be created in this way?

If you really inject all objects in this way, it is very likely that your code will become a shit. When there are dozens of dependencies in a class, you actually have to think, do I really need these things? Do I really need to inject it this way? Or is there a better way I can manage this injection?

If you look at the refactoring book, its author is very inclined not to use this method, but to use the constructor to manage the injection. Especially for small business classes, don't use that service method to inject.

After talking about injection, the next step is execution. How do you execute it? Let's take a look at our case just now. Is the execution parallel, or use an array as for each to execute? These are implementation details. You will find that in static modeling, no one will talk about this matter, only books like refactoring will talk about it.

Finally, if you feel that there is no way to understand the dynamics, you can replace it with [Runtime].

questioner:

That's right, I remembered that when I use go, I need to consider a series of problems of injecting objects at runtime, and I know what you are talking about.

2. Is case modeling a design pattern that changes the original design (various if-else) through a series of refactorings?

questioner:

Teacher, I came in relatively late. When I came in, you were already talking about that case. Although you two talked a lot, I still didn't understand the concept of dynamic modeling.

The process of writing the case you talked about gives me the feeling that it is a process of reconstruction. Refactor the original code design into a more abstract design. After the abstraction, I found that this is more like a design pattern (rather than knowing that there is a design pattern and then applying it), which is what you mean dynamic?

Answered by:

This is not yet. This is a result, I personally think that modeling must be a process. And a word I admire is called drive, which is driven, that is, you see a lot of DD (BDD, DDD, FDD) and the like.

If you tell me a result, it's usually something a master can do. You can tell what this should look like at a glance, but for ordinary people, we need a driver, which is equivalent to saying that you tell me 1 and 1, and then I can calculate 2, which is not bad.

What is the driver? This driving process is just like what you said, in our case, it is actually a refactoring, and it is found that refactoring is repeated.

Dynamic does not mean that the process I am modeling is dynamic, but what is my focus? You can see that my focus in this process is not on classes, attributes, methods, relationships between classes, responsibilities of classes, etc. All I care about is this object. What is the state of this object? Of course, we also consider the interaction between some objects, the relationship between objects, and the logic of objects. For example, if I call sequentially or for each, when was this object created, when was it initialized, how was it assembled, how was it structured, and how was it destroyed. This is the source of this dynamic.

I just said that if you find dynamics difficult to understand, you can think of it as runtime. But why isn't our name called runtime? It is for comparison with this static, which is the translated meaning of dynamic. I feel a little bit lost during the translation process. Back to your question, what is dynamic? What we care about is what happens and the state at runtime.

If you stick to the word, it does not mean that the modeling process is dynamic, but that the driving force of modeling is dynamic, and the driving force of modeling comes from these dynamic concepts (object, state, interaction, object relationship, business logic ), rather than from these static concepts (class, method, attribute, class and class relationship, class responsibility).

If you use this driving force to model, you will gradually discover some benefits, and you may get a better design. I hope that I feel that for ordinary programmers like us, we need some driving force to help us make better designs.

3. Is there a better way of dynamic modeling besides the timing diagram?

questioner:

The purpose of dynamic modeling needs to express some logic hidden in the static model. Can I use other models to show this interactive logic? Instead of using this sequence diagram form. Because I think the form of the sequence diagram, the granularity of the model at this method level is too fine.

Answered by:

In fact, I have been working for so many years, and frankly, I have not found any other better way. There is no other picture that can achieve the balance of this description so well. What does this balance mean? It means not describing too many details without missing details, and you can adjust flexibly at any time (for example, if I don't care about this condition, then I will remove it).

Regarding the problem of model granularity, you can actually do this modeling at a very high granularity. For example, I have a library system. I have a third-party system called Payment. I want to check whether the user has fined. If so, I cannot lend you books. This Payment can correspond to an interface or a class. In addition, assuming that there is an external system Splunk, we can call it through the notice. In fact, Splunk does not correspond to a class, but corresponds to a system. Whether this system is called through in process or http rest is allowed. of.

So, this is why I say that it is the best tool for balance. It is equivalent to spreading out on the horizontal axis and spreading out on the vertical axis, and it is the easiest way to understand.

16e153afcb704939c9d247466f2bc11a.png

Mr. Xiao's live video address:

Pay attention to the technical trivia of station B

e1fc1ea27d1e3357d4b72956a11bed26.png

如果喜欢本文
欢迎 在看丨留言丨分享至朋友圈 三连

 热文推荐  
 阿里留不住的P10毕玄,到底有多牛?
仿天猫商城项目,超级漂亮【附源码】,接私活必备
有没有完全自主的国产化数据库技术
毕玄:怎么提升写代码的能力 聊聊如何度过寒冬(公司篇)​蚂蚁P10玉伯的产品思考:技术人如何做产品

Guess you like

Origin blog.csdn.net/u013527895/article/details/127383739