Clean Code: chapter 11~12

The startup process is a concern that any application must address. It is the first concern
that we will examine in this chapter. The separation of concerns is one of the oldest
and most important design techniques in our craft .

 

Unfortunately, most applications don’t separate this concern. The code for the startup
process is ad hoc and it is mixed in with the runtime logic . Here is a typical example:

 

public Service getService() {
    if (service == null)
        service = new MyServiceImpl(...); // Good enough default for most cases?
    return service;
}
 

We don’t incur the overhead of construction unless we actually use the object, and our startup
times can be faster as a result
. We also ensure that null is never returned.

 

However, we now have a hard-coded dependency on MyServiceImpl and everything its
constructor requires (which I have elided). We can’t compile without resolving these
dependencies, even if we never actually use an object of this type at runtime!

 

One occurrence of LAZY-INITIALIZATION isn’t a serious problem, of course. However,
there are normally many instances of little setup idioms like this in applications
. Hence,
the global setup strategy (if there is one) is scattered across the application, with little
modularity and often significant duplication.

 

Perhaps worst of all, we do not know whether MyServiceImpl is the right object in all
cases. I implied as much in the comment . Why does the class with this method have to
know the global context? Can we ever really know the right object to use here? Is it even
possible for one type to be right for all possible contexts?

 

If we are diligent about building well-formed and robust systems, we should never let
little, convenient idioms lead to modularity breakdown . The startup process of object construction
and wiring is no exception . We should modularize this process separately from
the normal runtime logic
and we should make sure that we have a global, consistent strategy
for resolving our major dependencies .

 

our major dependecies may be this implementation class, may be that implementation class, it should not be wired in the program.

 



Again notice that all the dependencies point from main toward the OrderProcessing
application. This means that the application is decoupled from the details of how to
build a LineItem. That capability is held in the LineItemFactoryImplementation, which
is on the main side of the line. And yet the application is in complete control of when
the LineItem instances get built and can even provide application-specific constructor
arguments.

 

In the context of dependency management, an object should not take responsibility for instantiating dependencies itself .

 

JNDI lookups are a “partial ” implementation of DI, where an object asks a directory
server to provide a “service” matching a particular name.

 

MyService myService = (MyService)(jndiContext.lookup(“NameOfMyService”));

 

The invoking object doesn’t control what kind of object is actually returned (as long it
implements the appropriate interface, of course), but the invoking object still actively
resolves the dependency .

 

True Dependency Injection goes one step further . The class takes no direct steps to
resolve its dependencies
; it is completely passive. Instead, it provides setter methods or
constructor arguments (or both) that are used to inject the dependencies. During the construction
process, the DI container instantiates the required objects (usually on demand)
and uses the constructor arguments or setter methods provided to wire together the dependencies .
Which dependent objects are actually used is specified through a configuration
file or programmatically in a special-purpose construction module.

 

The Spring Framework provides the best known DI container for Java. You define
which objects to wire together in an XML configuration file, then you ask for particular
objects by name in Java code.

 

But what about the virtues of LAZY-INITIALIZATION? This idiom is still sometimes
useful with DI
.

 

The ephemeral nature of software systems makes this possible, as we will see. Let us first
consider a counterexample of an architecture that doesn’t separate concerns adequately .

 

The original EJB1 and EJB2 architectures did not separate concerns appropriately and
thereby imposed unnecessary barriers to organic growth . Consider an Entity Bean for a
persistent Bank class. An entity bean is an in-memory representation of relational data , in
other words, a table row.

 

EJB3 largely follows the Spring model of declaratively supporting cross-cutting concerns using
XML configuration files and/or Java 5 annotations.

 

If the persistence mapping details won’t change frequently , many teams may choose to keep the annotations , but with far fewer harmful drawbacks compared to the EJB2 invasiveness .

 

What if there were four simple rules that you could follow that would help you create good
designs as you worked? What if by following these rules you gained insights into the structure
and design of your code
, making it easier to apply principles such as SRP and DIP?
What if these four rules facilitated the emergence of good designs?

 

According to Kent, a design is “simple” if it follows these rules:
• Runs all the tests
• Contains no duplication
• Expresses the intent of the programmer
• Minimizes the number of classes and methods

The rules are given in order of importance.

 

Tight coupling makes it difficult to write tests. So, similarly, the more tests we write,
the more we use principles like DIP and tools like dependency injection, interfaces, and
abstraction to minimize coupling. Our designs improve even more .

 

Remarkably, following a simple and obvious rule that says we need to have tests and
run them continuously impacts our system’s adherence to the primary OO goals of low
coupling and high cohesion
. Writing tests leads to better designs.

 

The majority of the cost of a software project is in long-term maintenance . In order to
minimize the potential for defects as we introduce change, it’s critical for us to be able to
understand what a system does.

 

Therefore, code should clearly express the intent of its author . The clearer
the author can make the code
, the less time others will have to spend understanding it. This
will reduce defects and shrink the cost of maintenance .

 

You can express yourself by choosing good names . We want to be able to hear a class
or function name and not be surprised when we discover its responsibilities.


You can also express yourself by keeping your functions and classes small . Small
classes and functions are usually easy to name, easy to write, and easy to understand.

 

You can also express yourself by using standard nomenclature . Design patterns, for
example, are largely about communication and expressiveness. By using the standard
pattern names, such as COMMAND or VISITOR, in the names of the classes that implement
those patterns, you can succinctly describe your design to other developers.

 

Well-written unit tests are also expressive. A primary goal of tests is to act as documentation
by example. Someone reading our tests should be able to get a quick understanding
of what a class is all about.

 

But the most important way to be expressive is to try . All too often we get our code
working
and then move on to the next problem without giving sufficient thought to making
that code easy for the next person to read
. Remember, the most likely next person to read
the code will be you
.

 

So take a little pride in your workmanship . Spend a little time with each of your functions
and classes. Choose better names, split large functions into smaller functions, and
generally just take care of what you’ve created
. Care is a precious resource.

 

Our goal is to keep our overall system small while we are also keeping our functions
and classes small. Remember, however, that this rule is the lowest priority of the four rules
of Simple Design.
So, although it’s important to keep class and function count low, it’s
more important to have tests , eliminate duplication , and express yourself .

 

Is there a set of simple practices that can replace experience? Clearly not . On the other
hand, the practices described in this chapter and in this book are a crystallized form of the
many decades of experience enjoyed by the authors. Following the practice of simple
design can and does encourage and enable developers to adhere to good principles and
patterns
that otherwise take years to learn .

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【1】项目代码完整且功能都验证ok,确保稳定可靠运行后才上传。欢迎下载使用!在使用过程中,如有问题或建议,请及时私信沟通,帮助解答。 【2】项目主要针对各个计算机相关专业,包括计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网等领域的在校学生、专业教师或企业员工使用。 【3】项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 【4】如果基础还行,或热爱钻研,可基于此项目进行二次开发,DIY其他不同功能,欢迎交流学习。 【注意】 项目下载解压后,项目名字和项目路径不要用中文,否则可能会出现解析不了的错误,建议解压重命名为英文名字后再运行!有问题私信沟通,祝顺利! 基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值