Chapter 1 hello, lambda expressions! Section 2: the greatest achievement of functional programming
Function-style code has a higher signal-to-noise ratio. Less code is written, but more is done for each line or expression. Compared with imperative programming, functional programming has benefited us a lot:
This avoids explicit modification or assignment of variables, which are usually the root cause of bugs and makes it difficult to parallelize code. In command line programming, we assign values to the totalOfDiscountedPrices variable continuously in the loop body. In the function style, no explicit modification operations are performed on the code. The fewer variables, the fewer bugs in the code. Code in the functional style can be easily parallelized. If the calculation is time-consuming, we can easily execute the elements in the list concurrently. If we want to parallelize imperative code, we have to worry about the problems caused by modifying the totalOfDiscountedPrices variable concurrently. In function programming, we can only access this variable after full processing, eliminating the thread security risk. The code is more expressive. The imperative programming should be divided into several steps to explain what to do-create an initial value, traverse the price, add the discount price to the variable, and so on. In the function type, you only need to let the map method of the List return a new list containing the discount price and then accumulate it. Functional programming is more concise; compared with imperative programming, less code is required for the same results. More concise code means less code is written, less read, and less maintenance-is it concise to check the 7th page. Functional code is more intuitive-reading code is like describing a problem-it is easy to understand once we are familiar with the syntax. The map method executes the given function (calculate the discount price) for each element of the set, and then returns the result set, as shown in the demo.
Figure 1-after map executes a given function for each element in the set with a lambda expression, we can make full use of the power of functional programming in Java.
With the function style, you can write codes with better expressiveness, conciseness, less assignment operations, and fewer errors.
Supporting object-oriented programming is a major advantage of Java. Functional Programming and object-oriented programming are not excluded. The real style change is from command line programming to Declarative Programming. In Java 8, function and object-oriented can be effectively integrated. We can continue to use the OOP Style to model domain entities, their statuses, and relationships. In addition, we can also use functions for modeling behavior or state transition, workflow and data processing, and create composite functions.
Section 3: Why use the functional style?
We have seen the advantages of functional programming, but is it possible to use this new style? Is this just a small improvement, or is it a face change? There are still many practical questions to answer before we really spend time on it.
James asked: Is code less concise?
Simplicity is less than chaos. In the final analysis, it is necessary to express intentions effectively. It brings far-reaching benefits.
Writing code is like putting ingredients together. concise means you can turn ingredients into spices. It takes a lot of effort to write concise code. Less code is read, and truly useful code is transparent to you. A short code that is hard to understand or hide details can only be short rather than concise.
Simple code is really agile design. Concise Code removes the red tape. This means that we can try our ideas quickly. If it is good, we can continue. If it is not good, we can skip it quickly.
Writing code in Java is not difficult, and the syntax is simple. We also know the existing libraries and APIs well. What is really difficult is to use it to develop and maintain enterprise-level applications. We need to ensure that our colleagues close the database connection at the right time, and they will not keep occupying transactions, so they can correctly handle exceptions at the right layers, the lock can be correctly obtained and released, and so on.
None of these issues is a major concern. However, if it is combined with the complexity of the field, the problem becomes very difficult, and the development resources are tight and difficult to maintain. What if we encapsulate these policies into several small pieces of code and let them manage their own constraints? Then we don't have to spend any longer on implementing strategies. This is a huge improvement. Let's take a look at how functional programming works.
Crazy Iteration
We have been writing various iterations to process lists, sets, and maps. It is no longer common to use the iterator in Java, but it is too complicated. They not only occupy several lines of code, but also are difficult to encapsulate.
How do we traverse collections and print them? You can use a for loop. How can we filter out some elements from the set? The for loop is still used, but some other modifiable variables need to be added. After these values are selected, how can we use them to obtain the final values, such as the minimum, maximum, and average values? You have to recycle and modify the variable.
Such an iteration is a treasure. Currently, Java provides built-in iterators for many operations, such as only loop, map operations, filtered values, and reduce operations, there are also many convenient functions, such as the maximum and minimum values and average values.
In addition, these operations can be well combined, so we can assemble them together to implement business logic, which requires less simple code. The written code is highly readable because it is logically consistent with the order in which the problem is described. In the second chapter, we will see several examples on the 19th page of the use of collections. Such examples are also everywhere in this book.
Application policy applies to all enterprise applications. For example, we need to confirm that a certain operation has been properly performed for security authentication. We need to ensure that the transaction can be executed quickly and update and modify logs correctly. These tasks usually end up with a piece of common code on the server, which is similar to the following pseudo code:
Transaction transaction = getFromTransactionFactory();//... operation to run within the transaction ...checkProgressAndCommitOrRollbackTransaction();UpdateAuditTrail();
This solution has two problems. First, it usually leads to repetitive workload and increases maintenance costs. Second, it is easy to forget the exceptions that may be thrown out in the Business Code, which may affect the transaction lifecycle and modify log updates. Here we should use try and finally blocks for implementation, but whenever someone moves this code, we have to re-confirm that this policy is not broken. Another way is to remove the factory and put the code above it. Instead of getting the transaction object, you can pass the executed code to a well-maintained function, like this:
runWithinTransaction((Transaction transaction) -> { //... operation to run within the transaction ...});
This is a small step, but it saves a lot of trouble. This policy for checking status and updating logs is abstracted and encapsulated into the runWithinTransaction method. We send this method a piece of code that needs to be run in the transaction context. We don't have to worry about who forgot to perform this step or didn't handle the exception. The function that implements the policy has completed this operation. In chapter 5, we will introduce how to apply such a policy using lambda expressions. Scaling Policy
The policy looks ubiquitous. In addition to applying them, enterprise-level applications also need to expand them.
We hope to add or delete some operations through some configuration information. In other words, operations can be processed before the core logic of the module is executed. This is common in Java, but it needs to be taken into account and designed in advance.
Components to be extended usually have one or more interfaces. We need to carefully design interfaces and implement the hierarchical structure of classes. This may work well, but it leaves a lot of interfaces and classes to be maintained. Such a design can easily become cumbersome and difficult to maintain, and ultimately damage the original intention of expansion.
There is also a solution-functional interfaces and lambda expressions that we can use to design scalable policies. We do not have to create new interfaces or follow the same method name. We can focus more on the business logic to be implemented.Use lambda expressions for decoration.
Easy concurrency
When a large application is approaching a milestone, a serious performance problem suddenly emerges. The team quickly located the performance bottleneck in a large module that processes massive data. Some people in the team suggested that the system performance can be improved if the advantages of multiple cores are fully explored.
However, if this huge module is written in the old Java style, the joy of this suggestion will soon be shattered.
The team quickly realized that it would take a lot of effort to change this giant object from serial execution to parallel execution, increasing the complexity and easily causing multithreading-related bugs. Is there no better way to improve performance?
Is it possible that the serial and parallel code are the same, no matter whether you choose serial or parallel execution, Just Like clicking the switch to show the idea?
It sounds like this can only be done in nuniya, but if we use function-based development, it will all become a reality. The built-in iterator and functional style will clear the last obstacle to parallelism. The JDK design enables the switching of serial and parallel execution to be implemented only with a few unremarkable code changes. We will refer to the 145 page "the leap of parallel execution.
Storytelling
A large amount of things will be lost when the business needs become code implementation. The more data is lost, the higher the possibility of errors and the management cost.
If the code looks the same as describing the requirement, it will be easier to read and discuss with the requirement personnel, making it easier to meet their needs.
For example, you heard the product manager say, "Get the price of all stocks, find out the price of more than 500 yuan, and calculate the sum of assets that can be paid dividends ". Using the new facilities provided by Java, you can write as follows:
tickers.map(StockUtil::getprice).filter(StockUtil::priceIsLessThan500).sum()
This conversion process is almost lossless, because there is basically nothing to convert. This is the function type that plays a role. In this book, we will see more such examples, especially chapter 8th,
Use lambda expressions to build programs, 137 page. Focus isolation
In system development, the core business and the fine-grained logic required by it usually need to be isolated. For example, an order processing system wants to use different tax calculation policies for different transaction sources. Separating tax calculation from other processing logic will improve code reusability and scalability. In object-oriented programming, we call this concern isolation, which is usually solved by the Policy mode. The solution is to create some interfaces and implementation classes.
We can use less code to achieve the same effect. We can also quickly try our own product ideas, instead of having to come up with a bunch of code to get stuck. On page 63, we will use lambda expressions for attention isolation to further explore how to use lightweight functions to create this mode and perform attention isolation.
Evaluate inertia
When developing enterprise-level applications, we may interact with WEB Services, call databases, process XML, and so on. There are many operations to be performed, but not all of them are required at all times. To avoid some operations or at least delay some temporarily unnecessary operations is to improve performance or reduce program startup, the simplest way to respond to time.
This is only a small matter, but it takes some time to implement it in the pure OOP mode. To delay initialization of some heavyweight objects, we need to handle various object references, check for null pointers, and so on. However, if you use the new Optinal class and some functional APIs provided by it, this process will become very simple, the code will be clearer, and we will go to the 105 pageDelayed Initialization.
Improve Testability
The less the code processing logic, the less likely to be corrected. In general, functional code is easier to modify, and testing is also easier.
In addition, just like chapter 1,Use lambda expressions for DesignAnd Chapter 2Resource usageIn this way, lambda expressions can be used as a lightweight mock object to make exception testing clearer and easier to understand. Lambda expressions can also be used as a good testing tool. Many common test cases can accept and process lambda expressions. The written test cases can grasp the essence of the functions that require regression testing. At the same time, various implementations to be tested can be completed by inputting different lambda expressions.
JDK's own automated test cases are also a good example of lambda expressions. For more information, see the source code in the OpenJDK repository. Through these test programs, we can see how lambda expressions parameterize the key behaviors of test cases. For example, they are used to build test programs and "Create a result container ", then, "check some parameterized post-conditions ".
We have seen that functional programming not only allows us to write high-quality code, but also elegantly solves various difficulties in the development process. That is to say, development programs will become faster and simpler, with fewer errors-as long as you can follow the rules we will introduce later.
Not yet resumed. Follow deepinmind in subsequent articles.
For Original article reprinted, please indicate the source: Java translation site