Refining Uncle Bob’s Clean Code(一)

I’ve just finished reading ‘Uncle Bob’s’ new book ‘Clean Code‘. I fully agree with most of the statements and it was a pleasure to read, especially because Uncle Bob and his co-authors have a talent for putting some of the most relevant values and principles of software development into so simple words (i wished they have crossed my mind within more than one discussion in the past).

A book about ‘Clean Code‘ wouldn’t be a truly book about code if it wouldn’t contain some code. And yes, this book is full ofcode, surrounded by some useful ruminations and critical discussions on how to improve the given code – getting your feet wet and looking at some real world examples is just the flesh on the bones for a book about code.

As Uncle Bob encouraged the reader within the introduction of the book to ‘work hard while reading the book‘ in terms of thinking what’s right and what’s wrong about a given piece of code, so did i with his refined Args example at the end of Chapter 14 ‘Successive Refinement‘.

The Boy Scout Rule

Personally, i like the idea of Uncle Bob’s ‘Boy Scout Rule‘ – leaving the campground in a better state than you found it. So looking at a piece of code, you always take care of it, improving it if necessary, so that the normal tendency of code degeneration is interrupted but rather gets better and better over time.

When i first came across the code of the Args example (at the start of the chapter), i honestly wasn’t sure if this was already the refactored version or still the original version before refactoring (it turned out to be the refactored one). Don’t get me wrong, the givencode is in really good shape, but for some points i’m not sure if you still can improve readability and structure by applying some of Uncle Bobs principles (given in the book resp. some of the OO principles from his book ‘Agile Software Development‘).

So applying the the Boy Scout Rule to the Args example, the following sections will give some ruminations about the given code, the principles it may violate or miss, along with some suggestions on how to improve it.

Thanks, Uncle Bob !

Like Uncle Bob mentioned when discussing SerialDate (Chapter 16), each programmer shows a big portion of courage when offering hiscode to the community, abandoning it to discussion and critical review. Like Uncle Bob appreciated those traits to the author ofSerialDate, so it is to Uncle Bob. He immediately permits my question for the following review of his Args code and gave accreditation to present his refactored code. Thanks, Oncle Bob!

Clean Code

So without further ado, let’s take a closer look at Uncle Bob’s refined code. I will mainly show you the code of class Args, as it contains most of the logic i’m going to refine:

001 import java.util.Arrays;
002 import java.util.HashMap;
003 import java.util.HashSet;
004 import java.util.Iterator;
005 import java.util.List;
006 import java.util.Map;
007 import java.util.Set;
008  
009 public class Args {
010   private String schema;
011  
012   private Map<Character, ArgumentMarshaler> marshalers =
013     new HashMap<Character, ArgumentMarshaler>();
014   private Set<Character> argsFound = new HashSet<Character&amp>();
015   private Iterator<String> currentArgument;
016   private List<String> argsList;
017  
018   public Args(String schema, String[] args) throws ArgsException {
019     this.schema = schema;
020     argsList = Arrays.asList(args);
021     parse();
022   }
023  
024   private void parse() throws ArgsException {
025     parseSchema();
026     parseArguments();
027   }
028  
029   private boolean parseSchema() throws ArgsException {
030     for (String element : schema.split(",")) {
031       if (element.length() > 0) {
032         parseSchemaElement(element.trim());
033       }
034     }
035     return true;
036   }
037  
038   private void parseSchemaElement(String element) throws ArgsException {
039     char elementId = element.charAt(0);
040     String elementTail = element.substring(1);
041     validateSchemaElementId(elementId);
042     if (elementTail.length() == 0)
043       marshalers.put(elementId, new BooleanArgumentMarshaler());
044     else if (elementTail.equals("*"))
045       marshalers.put(elementId, new StringArgumentMarshaler());
046     else if (elementTail.equals("#"))
047       marshalers.put(elementId, new IntegerArgumentMarshaler());
048     else if (elementTail.equals("##"))
049       marshalers.put(elementId, new DoubleArgumentMarshaler());
050     else
051       throw new ArgsException(ArgsException.ErrorCode.INVALID_FORMAT, elementId, elementTail);
052   }
053  
054   private void validateSchemaElementId(char elementId) throws ArgsException {
055     if (!Character.isLetter(elementId)) {
056       throw new ArgsException(ArgsException.ErrorCode.INVALID_ARGUMENT_NAME, elementId, null);
057     }
058   }
059  
060   private void parseArguments() throws ArgsException {
061     for (currentArgument = argsList.iterator(); currentArgument.hasNext();) {
062       String arg = currentArgument.next();
063       parseArgument(arg);
064     }
065   }
066  
067   private void parseArgument(String arg) throws ArgsException {
068     if (arg.startsWith("-"))
069       parseElements(arg);
070   }
071  
072   private void parseElements(String arg) throws ArgsException {
073     for (int i = 1; i < arg.length(); i++)
074       parseElement(arg.charAt(i));
075   }
076  
077   private void parseElement(char argChar) throws ArgsException {
078     if (setArgument(argChar))
079       argsFound.add(argChar);
080     else {
081       throw new ArgsException(ArgsException.ErrorCode.UNEXPECTED_ARGUMENT, argChar, null);
082     }
083   }
084  
085   private boolean setArgument(char argChar) throws ArgsException {
086     ArgumentMarshaler m = marshalers.get(argChar);
087     if (m == null)
088       return false;
089     try {
090       m.set(currentArgument);
091       return true;
092     catch (ArgsException e) {
093       e.setErrorArgumentId(argChar);
094       throw e;
095     }
096   }
097  
098   public int cardinality() {
099     return argsFound.size();
100   }
101  
102   public String usage() {
103     if (schema.length() > 0)
104       return "-[" + schema + "]";
105     else
106       return "";
107   }
108  
109   public boolean getBoolean(char arg) {
110     ArgumentMarshaler am = marshalers.get(arg);
111     boolean b = false;
112     try {
113       b = am != null && (Boolean) am.get();
114     catch (ClassCastException e) {
115       b = false;
116     }
117     return b;
118   }
119  
120   public String getString(char arg) {
121     ArgumentMarshaler am = marshalers.get(arg);
122     try {
123       return am == null "" : (String) am.get();
124     catch (ClassCastException e) {
125       return "";
126     }
127   }
128  
129   public int getInt(char arg) {
130     ArgumentMarshaler am = marshalers.get(arg);
131     try {
132       return am == null 0 : (Integer) am.get();
133     catch (Exception e) {
134       return 0;
135     }
136   }
137  
138   public double getDouble(char arg) {
139     ArgumentMarshaler am = marshalers.get(arg);
140     try {
141       return am == null 0 : (Double) am.get();
142     catch (Exception e) {
143       return 0.0;
144     }
145   }
146  
147   public boolean has(char arg) {
148     return argsFound.contains(arg);
149   }
150 }

Separation of concerns

First of all, i’ve asked myself, why does class Args do so much? If you look at the code you can see at least two separate activities: Parse the given schema (tangled with the Selection of the related ArgumentMarshaler), followed by the iteration of the current arguments, including the determination of the potential argument Ids along with the population of the related argument values (belonging to the given argument id) to the responsible ArgumentMarshaler.

Is there maybe more than one reason to change that class, thus violating the Single Responsibility Principle? For example if you want to extend or change the notation of the schema definition, you surely have to reflect that fact in the parsing strategie of the schema. Similarly, if you want to pass the given arguments in another format (say within a hierarchy, or allowing a sophisticated argument chain), you also have to change the class.

Naming

Those two tasks aren’t closely coupled: The output of parsing the schema (a Set of appropriate ArgumentMarshaler) serves as input for processing the given arguments. So nothing would argue against separating those two tasks by releasing each of them in an own class, say ArgumentPopulator and MarshalersFactory (providing a Number of ArgumentMarshalers for a given schema – we’ll get back to them in a Minute).

Why should i name them this way? First of all, if you look at method parseSchema(), it gives you not to much hints about its effects. Surely, it’s gonna parse the given schema, but it doesn’t say anything about it’s intention, that is to identify and select a set of appropriate ArgumentMarshalers which are capable to handle the given arguments, specified by the schema. So parsing the schema is correct but only half the true, characterizing the pure action. The real intention is to retrieve those ArgumentMarshalers. Therefore it’s best located in a Factory for ArgumentMarshalers (as long as we don’t want to care about the concrete implementations), providing a method getMarshalersFor( schema ) that clearly states its intent (aka its effects).

Same goes for method parseArguments(). Again, its intention isn’t clear by looking at the methods name. We want to browse through the given arguments, that’s clear – but for what reason? Detecting the distinct arguments and passing the related argument values to the associated ArgumentMarshaler! In other words: we want to populate our ArgumentMarshaler with the given argument values – that’s the real intention behind the argument parsing.

Separate Constructing a System from Using it

As stated in chapter 11 ‘Systems‘, some of the famous enterprise frameworks nowadays, advocate the separation of setting up a System from Running the System. The Setup is done for example by inspecting a separate configuration file, where all classes and its dependencies are defined followed by the instantiation of those classes, including the ‘wiring’ of dependend beans (you can see this ‘pattern’ clearly when looking at the Spring framework for example). With this pattern comes Dependency Injection in a more or less normal way: The building block (or main) who’s is responsible for setting up the system is the only one who ‘sees’ all beans, thus can statisfy the needed dependencies of all beans by injecting them.

多源动态最优潮流的分布鲁棒优化方法(IEEE118节点)(Matlab代码实现)内容概要:本文介绍了基于Matlab实现的多源动态最优潮流的分布鲁棒优化方法,适用于IEEE118节点电力系统。该方法旨在应对电力系统中源荷不确定性带来的挑战,通过构建分布鲁棒优化模型,有效处理多源输入下的动态最优潮流问题,提升系统运行的安全性和经济性。文中详细阐述了模型的数学 formulation、求解算法及仿真验证过程,并提供了完整的Matlab代码实现,便于读者复现与应用。该研究属于电力系统优化调度领域的高水平技术复现,具有较强的工程实用价值。; 适合人群:具备电力系统基础知识和Matlab编程能力的研究生、科研人员及从事电力系统优化调度的工程技术人员,尤其适合致力于智能电网、鲁棒优化、能源调度等领域研究的专业人士。; 使用场景及目标:①用于电力系统多源环境下动态最优潮流的建模与求解;②支撑含可再生能源接入的电网调度决策;③作为鲁棒优化方法在实际电力系统中应用的教学与科研案例;④为IEEE118节点系统的仿真研究提供可复现的技术支持。; 阅读建议:建议结合提供的Matlab代码逐模块分析,重点关注不确定变量的分布鲁棒建模、目标函数构造及求解器调用方式。读者应具备定的凸优化和电力系统分析基础,推荐配合YALMIP工具包与主流求解器(如CPLEX、Gurobi)进行调试与扩展实验。
内容概要:本文系统介绍了物联网与云计算的基本概念、发展历程、技术架构、应用场景及产业生态。文章阐述了物联网作为未来互联网的重要组成部分,通过RFID、传感器网络、M2M通信等技术实现物理世界与虚拟世界的深度融合,并展示了其在智能交通、医疗保健、能源管理、环境监测等多个领域的实际应用案例。同时,文章强调云计算作为物联网的支撑平台,能够有效应对海量数据处理、资源弹性调度和绿色节能等挑战,推动物联网规模化发展。文中还详细分析了物联网的体系结构、标准化进展(如IEEE 1888、ITU-T、ISO/IEC等)、关键技术(中间件、QoS、路由协议)以及中国运营商在M2M业务中的实践。; 适合人群:从事物联网、云计算、通信网络及相关信息技术领域的研究人员、工程师、高校师生以及政策制定者。; 使用场景及目标:①了解物联网与云计算的技术融合路径及其在各行业的落地模式;②掌握物联网体系结构、标准协议与关键技术实现;③为智慧城市、工业互联网、智能物流等应用提供技术参考与方案设计依据;④指导企业和政府在物联网战略布局中的技术选型与生态构建。; 阅读建议:本文内容详实、覆盖面广,建议结合具体应用场景深入研读,关注技术标准与产业协同发展趋势,同时结合云计算平台实践,理解其对物联网数据处理与服务能力的支撑作用。
标题基于Java的停车场管理系统设计与实现研究AI更换标题第1章引言介绍停车场管理系统研究背景、意义,分析国内外现状,阐述论文方法与创新点。1.1研究背景与意义分析传统停车场管理问题,说明基于Java系统开发的重要性。1.2国内外研究现状综述国内外停车场管理系统的发展现状及技术特点。1.3研究方法以及创新点介绍本文采用的研究方法以及系统开发中的创新点。第2章相关理论总结Java技术及停车场管理相关理论,为系统开发奠定基础。2.1Java编程语言特性阐述Java的面向对象、跨平台等特性及其在系统开发中的应用。2.2数据库管理理论介绍数据库设计原则、SQL语言及在系统中的数据存储与管理。2.3软件工程理论说明软件开发生命周期、设计模式在系统开发中的运用。第3章基于Java的停车场管理系统设计详细介绍系统的整体架构、功能模块及数据库设计方案。3.1系统架构设计阐述系统的层次结构、模块划分及模块间交互方式。3.2功能模块设计介绍车辆进出管理、车位管理、计费管理等核心功能模块设计。3.3数据库设计给出数据库表结构、字段设计及数据关系图。第4章系统实现与测试系统实现过程,包括开发环境、关键代码及测试方法。4.1开发环境与工具介绍系统开发所使用的Java开发环境、数据库管理系统等工具。4.2关键代码实现展示系统核心功能的部分关键代码及实现逻辑。4.3系统测试方法与结果阐述系统测试方法,包括单元测试、集成测试等,并展示测试结果。第5章研究结果与分析呈现系统运行效果,分析系统性能、稳定性及用户满意度。5.1系统运行效果展示通过截图或视频展示系统实际操作流程及界面效果。5.2系统性能分析从响应时间、吞吐量等指标分析系统性能。5.3用户满意度调查通过问卷调查等方式收集用户反馈,分析用户满意度。第6章结论与展望总结研究成果,提出系统改进方向及未来发展趋势。6.1研究结论概括基于Java的停车场管理
给定的参考引用中未涉及“Staged Projection Refining Multiple Orthogonal Matching Pursuit Algorithm”的相关信息,因此无法依据引用内容对该算法进行介绍及说明其应用。 般而言,正交匹配追踪(Orthogonal Matching Pursuit, OMP)算法是种用于稀疏信号恢复的贪婪算法,它通过迭代地选择与残差最相关的原子来逐步构建稀疏表示。而“Staged Projection Refining Multiple Orthogonal Matching Pursuit Algorithm”可能是在传统OMP算法基础上进行改进和扩展的算法。 从名称推测,“Staged Projection”可能意味着该算法采用分阶段投影的方式,逐步优化投影过程以提高稀疏信号恢复的准确性;“Refining”表示对结果进行精炼,可能是在迭代过程中不断调整和优化已选择的原子;“Multiple”可能表示该算法在每次迭代中会选择多个原子,而不是像传统OMP每次只选个原子。 该算法可能应用于信号处理、图像处理、机器学习等领域的稀疏信号恢复问题,例如在压缩感知中用于从少量测量值中恢复原始的稀疏信号,在图像去噪、特征提取等方面也可能有应用。 以下是个简单的传统OMP算法的Python代码示例: ```python import numpy as np def omp(A, y, K): m, n = A.shape r = y.copy() omega = [] x = np.zeros(n) for k in range(K): correlations = np.abs(A.T @ r) idx = np.argmax(correlations) omega.append(idx) A_omega = A[:, omega] x_omega = np.linalg.pinv(A_omega) @ y r = y - A_omega @ x_omega x[omega] = x_omega return x # 示例使用 m = 50 n = 100 K = 10 A = np.random.randn(m, n) x_true = np.zeros(n) x_true[np.random.choice(n, K, replace=False)] = np.random.randn(K) y = A @ x_true x_est = omp(A, y, K) ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值