Note of CLEAN CODE chapter 3 - Function

Small

Functions should be small. You can fit about 150 characters in a line and 100 lines in a screen, so

  • A line should not contain more than 150 characters
  • A function should not be 100 lines long()

for example in Listing 3-3

public static String renderPageWithSetupsAndTeardowns(
    PageData pageData, boolean inSuite) throws Exception{
    if(isTest(pageData)){
        includeSetupAndTeardownPages(pageData, isSuite);
    }
    return pageData.getHtml();
}

block and indenting

As we can see in the example

  • blocks within if when for statements and so on should be one line long;

includeSetupAndTeardownPages(pageData, isSuite);

Functions should be large enough to hold nested structures;

  • indent level of a function should not be greater than 1 or 2

Do one thing

Make sure your function only does one thing, which does steps that are only one level below the stated name of the function.

Purpose of it is to compose a larger concepot into the next level of abstraction.

How to know your function is doing more than one thing?

  • Make sure you can extract a function from it with a name that isn’t merely restatement of the implementation.

One Level of Abstraction per Function

reading code from top to bottom - Stepdown Rule

  • include setups and teardowns, we include setups, then testpage content, teardowns
    • include setups, we include suite setup if this is a suite, the regular setup
      • to include the suite setup serach parent hierarchy for …

make it like a tree.

Switch

Try to bury switch statements in a low-level class and never repeat them.
Bury them into an ABSTRACT FACTORY and use INHERITANCE and POLYMORPHISM, and the rest of the system can never read them.

//original
public Money calculatePay(Employee e) throws InvalidEmployeeType{
    switch(e.type){
        case COMMISSIONED:
            return calComissionPay();
        case HOURLY:
            return calHourlyPay();
            ...
    }
}

Codes above do more than one thing, and they will grow, and they violate SRP, OCP;

//ABSTRACT
public abstract class Employee(){
    public abstract boolean isPayday();
    public abstract Money calculatePay();
    public abstract void deliveryPay(Money money);
}


public interface EmployeeFactory(){
    public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType;
}


public class EmployeeFactoryImpl() implements EmployeeFactory{
    public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType{
        switch(r.type){
            case COMMISSIONED:
                return new CommissionedEmployee(r);
            case HOURLY:
                return new HourlyEmployee(r);
                ...
        }
    }
}

Descriptive Name

Do not be afraid to make the name long

  • spend time choosing a name
  • descriptive names will clarify the design
  • be consistent in your names

Function Aruguments

More than three aruguments requires special justification

And output aruguments are harder to understand – cause us to do a double-take

Monadic Form

  • First, make it clear to know it is a event
  • If the function will change the input arugument, you are supposed to return it
    StringBuffer transform(StringBuffer in) is better than void transform(StringBuffer in)

Flag Argument

Flag arguments are ugly, and it will make function do more than one thing, so if you need to distinguish if statements, just get rid of the flag arguments and write two different functions.

Dyadic Functions and Triads

Convert them into monads or make the arguments in a natural order.

Argument Objects

if a function needs more than three arguments, they are supposed to be wrapped into a class.
makeCircle(int x, int y, int radius) can be turned to makeCircle(Point center, int radius)

Argument List

When we want to pass a variable number of arguments into a function, we can make them equivalent to a single argument of type List like the String.format() function

Verbs and Keywords

Function can use verb/noun form to include arguments. Like writeField(name) might be better than write(name). And assertExpectedEqualsActual(expect, actual) might be better than assertEquals

Have no Side Effects

When you check a user password, only check it and DO NOT initialize it.(initialize is the side effect, and it might be coupling).
If someone call it inadvertantly, it will make the session data lost.

If you have to add initialization to the function, rename it! like checkPasswordAndInitSession()(but it violates do one thing principle)

Output Arguments

when you want to output sth, it is not supposed to use it as a argument like
public void appendFooter(StringBuffer report), you can just change it as report.appendFooter()

Output arguments should be avoided

Command query Separation

A function either change the data state, or return some data information.
Avoid doing both at the same time like boolean set(String id, String name), which returns true when it successfully set the name and returns false to mean the id does not exist.

But it is not readable for readers when they see the codes

if(set("10", "bob")){
    ...
}

maybe it is better to seperate them

if(userIdExists("10")){
    setUserName("10", "bob");
    ...
}

Prefer Exception to Error Codes

Throwing Exceptions might make your code cleaner compared to return error codes.

Extract the try/catch blocks

package your code and throw exception, then try/catch it at the most abstract level

public void delete(Page){
    try{
        deletePageAndAllReference(page);
    }catch(Exception e){
        log.info(e.getMessage());
    }
}

public void deletePageAndAllReference(Page page){
    page.delete();
    register.deletePageReference(page.name);
    configKeys.deleteKey(page.name.makeKey());
}

Error Handling is One Thing

there should be nothing after catch/finally blocks

Error.java Dependency Magnet

Returning codes are basically defined in some enum or class

When the enum/class is changed, all other classes dependent on it will be recompiled and redeployed, so it will cause more burden.

Don’t repeat yourself

make the repeat statements become functions

Structured Programming

Edsger Dijkstra said

  • every function/block within a function should have one entry and one exit
    • one return in a function
    • never break or continue in a loop
    • never ever use goto

It is hard, so occasional multiple return, break, continue statements do no harm if you keep your functions small, and sometimes they might be more expressive.

How to do it?

  1. first draft(even if it is clumsy and idsorganized)
  2. wordsmith, restructe and refine your draft
  3. mention
    1. indenting and nested loops
    2. long argument lists
    3. name
    4. duplicated code
  4. action
    1. split out functions
    2. change names
    3. eliminate duplications
    4. shrink and reorder method
请告诉我具体的实验步骤: Lab: system calls In the last lab you used system calls to write a few utilities. In this lab you will add some new system calls to xv6, which will help you understand how they work and will expose you to some of the internals of the xv6 kernel. You will add more system calls in later labs. Before you start coding, read Chapter 2 of the xv6 book, and Sections 4.3 and 4.4 of Chapter 4, and related source files: • The user-space "stubs" that route system calls into the kernel are in user/usys.S, which is generated by user/usys.pl when you run make. Declarations are in user/user.h • The kernel-space code that routes a system call to the kernel function that implements it is in kernel/syscall.c and kernel/syscall.h. • Process-related code is kernel/proc.h and kernel/proc.c. To start the lab, switch to the syscall branch: $ git fetch $ git checkout syscall $ make clean If you run make grade you will see that the grading script cannot exec trace and sysinfotest. Your job is to add the necessary system calls and stubs to make them work. Using gdb (easy) In many cases, print statements will be sufficient to debug your kernel, but sometimes being able to single step through some assembly code or inspecting the variables on the stack is helpful. To learn more about how to run GDB and the common issues that can arise when using GDB, check out this page. To help you become familiar with gdb, run and then fire up gdb in another window (see the gdb bullet on the guidance page). Once you have two windows open, type in the gdb window: make qemu-gdb (gdb) b syscall Breakpoint 1 at 0x80002142: file kernel/syscall.c, line 243. (gdb) c Continuing. [Switching to Thread 1.2] Thread 2 hit Breakpoint 1, syscall () at kernel/syscall.c:243 243 { (gdb) layout src (gdb) backtrace The layout command splits the window in two, showing where gdb is in the source code. The backtrace prints out the stack backtrace. See Using the GNU Debugger for helpful GDB commands. Answer the following questions in answers-syscall.txt. Looking at the backtrace output, which function called syscall? Type a few times to step past struct proc *p = myproc(); Once past this statement, type , which prints the current process's proc struct (see kernel/proc.h>) in hex. np /x *p What is the value of p->trapframe->a7 and what does that value represent? (Hint: look user/initcode.S, the first user program xv6 starts.) The processor is running in kernel mode, and we can print privileged registers such as sstatus (see RISC-V privileged instructions for a description): (gdb) p /x $sstatus What was the previous mode that the CPU was in? In the subsequent part of this lab (or in following labs), it may happen that you make a programming error that causes the xv6 kernel to panic. For example, replace the statement num = p->trapframe->a7; with num = * (int *) 0; at the beginning of syscall, run , and you will see something similar to: make qemu xv6 kernel is booting hart 2 starting hart 1 starting scause 0x000000000000000d sepc=0x000000008000215a stval=0x0000000000000000 panic: kerneltrap Quit out of qemu. To track down the source of a kernel page-fault panic, search for the sepc value printed for the panic you just saw in the file kernel/kernel.asm, which contains the assembly for the compiled kernel. Write down the assembly instruction the kernel is panicing at. Which register corresponds to the variable num? To inspect the state of the processor and the kernel at the faulting instruction, fire up gdb, and set a breakpoint at the faulting epc, like this: (gdb) b *0x000000008000215a Breakpoint 1 at 0x8000215a: file kernel/syscall.c, line 247. (gdb) layout asm (gdb) c Continuing. [Switching to Thread 1.3] Thread 3 hit Breakpoint 1, syscall () at kernel/syscall.c:247 Confirm that the faulting assembly instruction is the same as the one you found above. Why does the kernel crash? Hint: look at figure 3-3 in the text; is address 0 mapped in the kernel address space? Is that confirmed by the value in scause above? (See description of scause in RISC-V privileged instructions) Note that scause was printed by the kernel panic above, but often you need to look at additional info to track down the problem that caused the panic. For example, to find out which user process was running when the kernel paniced, you can print out the process's name: (gdb) p p->name What is the name of the binary that was running when the kernel paniced? What is its process id (pid)? This concludes a brief introduction to tracking down bugs with gdb; it is worth your time to revisit Using the GNU Debugger when tracking down kernel bugs. The guidance page also has some other other useful debugging tips.
11-16
下载方式:https://pan.quark.cn/s/a4b39357ea24 布线问题(分支限界算法)是计算机科学和电子工程领域中一个广为人知的议题,它主要探讨如何在印刷电路板上定位两个节点间最短的连接路径。 在这一议题中,电路板被构建为一个包含 n×m 个方格的矩阵,每个方格能够被界定为可通行或不可通行,其核心任务是定位从初始点到最终点的最短路径。 分支限界算法是处理布线问题的一种常用策略。 该算法与回溯法有相似之处,但存在差异,分支限界法仅需获取满足约束条件的一个最优路径,并按照广度优先或最小成本优先的原则来探索解空间树。 树 T 被构建为子集树或排列树,在探索过程中,每个节点仅被赋予一次成为扩展节点的机会,且会一次性生成其全部子节点。 针对布线问题的解决,队列式分支限界法可以被采用。 从起始位置 a 出发,将其设定为首个扩展节点,并将与该扩展节点相邻且可通行的方格加入至活跃节点队列中,将这些方格标记为 1,即从起始方格 a 到这些方格的距离为 1。 随后,从活跃节点队列中提取队首节点作为下一个扩展节点,并将与当前扩展节点相邻且未标记的方格标记为 2,随后将这些方格存入活跃节点队列。 这一过程将持续进行,直至算法探测到目标方格 b 或活跃节点队列为空。 在实现上述算法时,必须定义一个类 Position 来表征电路板上方格的位置,其成员 row 和 col 分别指示方格所在的行和列。 在方格位置上,布线能够沿右、下、左、上四个方向展开。 这四个方向的移动分别被记为 0、1、2、3。 下述表格中,offset[i].row 和 offset[i].col(i=0,1,2,3)分别提供了沿这四个方向前进 1 步相对于当前方格的相对位移。 在 Java 编程语言中,可以使用二维数组...
源码来自:https://pan.quark.cn/s/a4b39357ea24 在VC++开发过程中,对话框(CDialog)作为典型的用户界面组件,承担着与用户进行信息交互的重要角色。 在VS2008SP1的开发环境中,常常需要满足为对话框配置个性化背景图片的需求,以此来优化用户的操作体验。 本案例将系统性地阐述在CDialog框架下如何达成这一功能。 首先,需要在资源设计工具中构建一个新的对话框资源。 具体操作是在Visual Studio平台中,进入资源视图(Resource View)界面,定位到对话框(Dialog)分支,通过右键选择“插入对话框”(Insert Dialog)选项。 完成对话框内控件的布局设计后,对对话框资源进行保存。 随后,将着手进行背景图片的载入工作。 通常有两种主要的技术路径:1. **运用位图控件(CStatic)**:在对话框界面中嵌入一个CStatic控件,并将其属性设置为BST_OWNERDRAW,从而具备自主控制绘制过程的权限。 在对话框的类定义中,需要重写OnPaint()函数,负责调用图片资源并借助CDC对象将其渲染到对话框表面。 此外,必须合理处理WM_CTLCOLORSTATIC消息,确保背景图片的展示不会受到其他界面元素的干扰。 ```cppvoid CMyDialog::OnPaint(){ CPaintDC dc(this); // 生成设备上下文对象 CBitmap bitmap; bitmap.LoadBitmap(IDC_BITMAP_BACKGROUND); // 获取背景图片资源 CDC memDC; memDC.CreateCompatibleDC(&dc); CBitmap* pOldBitmap = m...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值