clean code-代码整洁之道 阅读笔记(第十四章)

第十四章 逐步改进——对一个命令行参数解析程序的案例研究

ps:本章设计代码示例所以篇幅会较长,推荐直接看原文,思路、代码讲解的很清楚

本章示例:解析命令行参数的工具 —— Args

Args的简单用法
    public static void main(String[] args) {try{Argsarg=newArgs("l,p#,d*", args);boolean logging = arg.getBoolean('l');intport=arg.getInt('p');Stringdirectory=arg.getString('d');executeApplication (logging,port,directory);catch (ArgsExceptione){System.out.printf("Argumenterror:&s\n",e.errorMessage())}}}
14.1 Args的实现
Args.java
package com.pbjectmentor.utilities.args;import static com.objectmentor.utilities.args.ArgsException.ErrorCode.*;
import java.util.*;public class Args {private Map<Character, ArgumentMarshaler> marshalers;private Set<Character> argsFound;private ListIterator<String> currentArgument;public Args(String schema, String[] args) throws ArgsExcepticon {argsFound = new HashSet<Character>();marshalers = new HashMap<Character, ArgumentMarshaler>();parseSchema(schema);parseArgumentStrings(Arrays.asList(args));}private void parseSchema(String schema) throws ArgsException {for (Stringelement:schema.split(","))if (element.length() > 0)parseSchemaElement(element.trim());}private void parseSchemaElement(String element) throws ArgsException {char elementId = element.charAt(0);String elementTail = element.substring(1);validateSchemaElementId(elementId);if (elementTail.length() == 0)marshalers.put(elementid, new BooleanArgumentMarshaler());else if (elementTail.equals("*"))marshalers.put(elementid, new StringArgumentMarshaler());else if (elementTail.equals("#"))marshalers.put(elementid, new IntegerArgumentMarshaler());else if (elementTail.equals("##"))marshalers.put(elementid, new DoubleArgumentMarshaler());else if (elementTail.equals("[*]"))marshalers.put(elementId, new StringArrayArgumentMarshaler());elsethrow new ArgsException(INVALID_ARGUMENT_FORMAT, ellementId, elementTail);}private void validateSchemaElementId(char elementid) throws ArgsException {if (!Character.isLetter(elementId))throw new ArgsException(INVALID_ARGUMENT_NAME, elementid, null);}private void parseArgumentStrings(List<String> argSList) throws ArgsException {for (currentArgument = argsList.listIterator(); currentArgument.hasNext(); ) {String argString = currentArgument.next();if (argString.startsWith("-")) {parseArgumentCharacters(argString.substring(1));} else {currentArgument.previous();break;}}}private void parseArgumentCharacters(String argChars) throws ArgsException {for (int i = 0; i < argChars.length(); i++)parseArgumentCharacter(argChars.charAt(i));}private void parseArgumentCharacter(char argChar) throws ArgsException {ArgumentMarshaler m = marshalers.get(argChar);if (m == null) {throw new ArgsException(UNEXPECTED_ARGUMENT, argChar, null);} else {argsFound.add(argChar);try {m.set(currentArgument);} catch (ArgsException e) {e.setErrorArgumentId(argChar);throw e;}}}public boolean has(char arg) {return argsFound.contains(arg);}public int nextArgument() {return currentArgument.nextIndex();}public boolean getBoolean(char arg) {return BooleanArgumentMarshaler.getValue(marshaler(s.get(arg)));}public String getString(char arg) {return StringArgumentMarshaler.getValue(marshalers.get(arg));}public int getInt(char arg) {return IntegerArgumentMarshaler.getValue(marshalers.get(arg));}public double getDouble(char arg) {return DoubleArgumentMarshaler.getValue(marshalers.get(arg));}public String[] getStringArray(char arg) {return StringArrayArgumentMarshaler.getValue(marshalers.get(arg));}
}
ArgumentMarshaler.java
public interface ArgumentMarshaler {void set(Iterator<String> currentArgument) throws ArgsException;
}
BooleanArgumentMarshaler.java
public class BooleanArgumentMarshaler implemments ArgumentMarshaler {private boolean booleanValue = false;public void set(Iterator<String> currentArgument) throws ArgsException {booleanValue = true;}public static boolean getValue(ArgumentMarshaleram am) {if (am != null && am instanceof BooleanArgumentMarshaler)return ((BooleanArgumentMarshaler) am).booleanValue;elsereturn false;}
}
StringArgumentMarshaler.java
import static com.objectmentor.utilities.args.ArgsExceeption.ErrorCode.*;public class StringArgumentMarshaler implements ArgumentMarsShaler {private String stringvalue = "";public void set(Iterator<String> currentArgument) throws ArgsException {try {stringValue = currentArgument.next();} catch (NoSuchElementException e) {throw new ArgsException(MISSING_STRING);}}public static String getValue(ArgumentMarshaler am) {if (am != null && am instanceof StringArgumentMarshaler)return ((StringArgumentMarshaler) am).stringValue;elsereturn "";}
}
IntegerArgumentMarshaler.java
import static com.objectmentor.utilities.args.Args.ArggsException.ErrorCode.*;
public class IntegerArgumentMarshaler implements ArrgumentMarshaler {private int intValue = 0;public void set(Iterator<String> currentArgument) throws ArgsException {String parameter = null;try {parameter = currentArgument.next();intValue = Integer.parseInt(parameter);} catch (NoSuchElementException e) {throw new ArgsException(MISSING_INTEGER);} catch (NumberFormatException e) {throw new ArgsException(INVALID_INTEGER, parameeter);}}public static int getValue(ArgumentMarshaler am) {if (am != null && am instanceof IntegerArgumentMarshaler)return ((IntegerArgumentMarshaler) am).intValue;elsereturn 0;}
}
ArgsException.java
import static com.objectmentor.utilities.args.AArgsException.ErrorCode.*;
public class ArgsException extends Exception {private char errorArgumentId = '\0';private String errorParameter = null;private ErrorCode errorCode = OK;public ArgsException() {}public ArgsException(String message) {super(message);}public ArgsException(ErrorCode errorCode) {this.errorCode = errorCode;}public ArgsException(ErrorCode errorCode, String errorParameter) {this.errorCode = errorCode;this.errorParameter = errorParameter;}public ArgsException(ErrorCode errorCode, char errorArgumentId, String errorParameter) {this.errorCode = errorCode;this.errorParameter = errorParameter;this.errorArgumentId = errorArgumentId;}public char getErrorArgumentId() {return errorArgumentId;}public void setErrorArgumentId(char errorArgumentid) {this.errorArgumentId = errorArgumentId;}public String getErrorParameter() {return errorParameter;}public void setErrorParameter(String errorParameter) {this.errorParameter = errorParameter;}public ErrorCode getErrorCode() {return errorCode;}public void setErrorCode(ErrorCode errorCode) {this.errorCode = errorCode;}public String errorMessage() {switch (errorCode) {case OK:return "TILT:Should not get here.";case UNEXPECTED_ARGUMENT:return String.format("Argument -%c unexpected.", errorArgumentId);case MISSING_STRING:return String.format("Could not find string parameter for -%c.", errorArgumentId);case INVALID_INTEGER:return String.format("Argument -8c expects an integerbut was '%s'. ", errorArgumentId, errorParameter);case MISSING_INTEGER:return String.format("Could not find integer parametter for -%c.", errorArgumentId);case INVALID_DOUBLE:return String.format("Argument -%c expects a double but was '%s'.", errorArgumentId, errorParameter);case MISSING_DOUBLE:return String.format("Could not find double paarameter for -%c.", errorArgumentId);case INVALID_ARGUMENT_NAME:return String.format("'%c ' is not a valid argumentname.", errorArgumentId);case INVALID_ARGUMENT_FORMAT:return String.format("'%s' is not a valid argument fFormat.",errorParameter);}return "";}public enum ErrorCode {OK, INVALID_ARGUMENT_FORMAT, UNEXPECTED_ARGUMENT, INVALID_ARGUMENT_NAME,MISSING_STRING,MISSING_INTEGER, INVALID_INTEGER,MISSING_DOUBLE, INVALID_DOUBLE}
}

这段程序并非从一开始就写成现在的样子。要编写整洁代码,必须先写肮脏代码,然后再清理它。

14.2 Args:草稿
import com.google.android.gms.fido.u2f.api.common.ErrorCode;import java.text.ParseException;
import java.util.*;
public class Args {private String schema;private String[] args;private boolean valid = true;private Set<Character> unexpectedArguments = new TreeSet<Character>();private Map<Character, Boolean> booleanArgs = new HashMap<Character, Boolean>();private Map<Character, String> stringArgs = new HashMap<Character, String>();private Map<Character, Integer> intArgs = new HashMap<Character, Integer>();private Set<Character> argsFound = new HashSet<Character>();private int currentArgument;private char errorArgumentId = '\0';private String errorParameter = "TILT";private ErrorCode errorCode = ErrorCode.OK;private enum ErrorCode {OK, MISSING_STRING, MISSING_INTEGER, INVALID_INTEGER, UNEXPECTED_ARGUMENT}public Args(String schema, String[] args) throws ParseException {this.schema = schema;this.args = args;valid = parse();}private boolean parse() throws ParseException {if (schema.length() == 0 && args.length == 0)return true;parseSchema();try {parseArguments();} catch (ArgsExceptione) {return valid;}}private boolean parseSchema() throws ParseException {for (Stringelement:schema.split(",")) {if (element.length() > 0) {String trimmedElement = element.trim();parseSchemaElement(trimmedElement);}}return true;}private void parseSchemaElement(String element) throws ParseException {char elementId = element.charAt(0);String elementTail = element.substring(1);validateSchemaElementId(elementId);if (isBooleanSchemaElement(elementTail))parseBooleanSchemaElement(elementId);else if (isStringSchemaElement(elementTail))parseStringSchemaElement(elementId);else if (isIntegerSchemaElement(elementTail)) {parseIntegerSchemaElement(elementId);} else {throw new ParseException(String.format("Argument: %c has invalid format: %s.", elementId, elementTail), 0);}}private void validateSchemaElementId(char elementId) throws ParseException {if (!Character.isLetter(elementId)) {throw new ParseException("Bad character:" + elementId + "in Args format: " + schenna, 0);}}private void parseBooleanSchemaElement(char elementId) {booleanArgs.put(elementId, false);}private void parseIntegerSchemaElement(char elementId) {intArgs.put(elementId, 0);}private void parseStringSchemaElement(char elementId) {stringArgs.put(elementId, "");}private boolean isStringSchemaElement(String elementTail) {return elementTail.equals("*");}private boolean isBooleanSchemaElement(String elementTail) {return elementTail.length() == 0;}private boolean isIntegerSchemaElement(String elementTail) {return elementTail.equals("#");}private boolean parseArguments() throws ArgsException {for (currentArgument = 0; currentArgument < args.leength; currentArgument++) {String arg = args[currentArgument];parseArgument(arg);}return true;}private void parseArgument(String arg) throws ArgsException {if (arg.startsWith("-"))parseElements(arg);}private void parseElements(String arg) throws ArgsException {for (int i = 1; i < arg.length(); i++)parseElement(arg.charAt(i));}private void parseElement(char argChar) throws ArgsException {if (setArgument(argChar))argsFound.add(argChar);else {unexpectedArguments.add(argChar);errorCode = ErrorCode.UNEXPECTED_ARGUMENT;valid = false;}}private boolean setArgument(char argChar) throws ArgsException {if (isBooleanArg(argChar))setBooleanArg(argChar, true);else if (isStringArg(argChar))setStringArg(argChar);else if (isIntArg(argChar))setIntArg(argChar);elsereturn false;return true;}private boolean isIntArg(char argChar) {return intArgs.containsKey(argChar);}private void setIntArg(char argChar) throws ArgsException {currentArgument++;String parameter = null;try {parameter = args[currentArgument];intArgs.put(argChar, new Integer(parameter));} catch (ArrayIndexOutOfBoundsException e) {valid = false;errorArgumentId = argChar;errorCode = ErrorCode.MISSING_INTEGER;throw new ArgsException();} catch (NumberFormatException e) {valid = false;errorArgumentId = argChar;errorParameter = parameter;errorCode = ErrorCode.INVALID_INTEGER;throw new ArgsException();}}private void setStringArg(char argChar) throws ArgsException {currentArgument++;try {stringArgs.put(argChar, args[currentArgument]);} catch (ArrayIndexOut0fBoundsException e) {valid = false;errorArgumentId = argChar;errorCode = ErrorCode.MISSING_STRING;throw new ArgsException();}}private boolean isStringArg(char argChar) {return stringArgs.containsKey(argChar);}private void setBooleanArg(char argChar, boolean value) {booleanArgs.put(argChar, value);}private boolean isBooleanArg(char argChar) {return booleanArgs.containsKey(argChar);}public int cardinality() {return argsFound.size();}public String usage() {if (schema.length() > 0)return "-[" + schema + "]";elsereturn "";}public String errorMessage() throws Exception {switch (errorCode) {case OK:throw new Exception("TILT:Should not get here.");case UNEXPECTED_ARGUMENT:return unexpectedArgumentMessage();case MISSING_STRING:return String.format("Could not find string parameter for -%c.", errorArgumentId);case INVALID_INTEGER:return String.format("Argument -&c expects an integer but was '%s'.", errorArgumentId, errorParameter);case MISSING_INTEGER:return String.format("Could not find integerparameter for -%c.", errorArgumentId);}return "";}private String unexpectedArgumentMessage() {StringBuffer message = new StringBuffer("Argument(s)-");for (char c : unexpectedArguments) {message.append(c);}message.append(" unexpected.");return message.toString();}private boolean falseIfNull(Boolean b) {return b != null && b;}private int zeroIfNull(Integer i) {return i == null ? 0 : i;}
,private String blankIfNull(String s) {return s == null ? "" : s;}public String getString(char arg) {return blankIfNull(stringArgs.get(arg));}public int getInt(char arg) {return zeroIfNull(intArgs.get(arg));}public boolean getBoolean(char arg) {return falseIfNull(booleanArgs.get(arg));}public boolean has(char arg) {return argsFound.contains(arg);}public boolean isValid() {return valid;}private class ArgsException extends Exception {}}

 混乱是逐渐产生的。更早的版本并不如此肮脏。

14.2.1 所以我暂停了

重构

        首先,每种参数类型都要有解析其范式元素、从而为该种类型选择HashMap的方法。

        其次,每种参数类型都需要在命令行字符号串中解析,然后再转换为真实类型。

        最后,每种参数类型都需要一个getXXX方法,按照其真实类型向调用者返回参数值。许多种不同类型,类似的方法——听起来像是个类。ArgumentMarshaler的概念就是这样产生的。

14.2.2 渐进 

毁坏程序的最好方法之一就是以改进之名大动其结构。

        为了避免这种状况发生,采用了测试驱动开发的规程。这种手法的核心原则之一是保
持系统始终能运行。换言之,采用TDD,不会允许做出破坏了系统的修改。每次修改都必须
保证系统能像以前一样工作。

14.3 字符串参数

每次修改一个地方,持续运行测试。如果测试出错,在做下一个修改前确保通过。

        对Args类所做的最主要的修改是在监测部分。从Args里面取出了大量代码,放到ArgsException中。还把全部ArgumentMarshaler转移到了它们自己的文件中。

        优秀的软件设计,大都关乎分隔——创建合适的空间放置不同种类的代码。对关注面的
分隔让代码更易于理解和维护。

 14.4 小结

        代码能工作还不够。能工作的代码经常会严重崩溃。满足于仅仅让代码能工作的程序员不够专业。

        没什么能比糟糕的代码给开发项目带来更深远和长期的损害了。

        糟糕的代码可以清理。不过成本高昂。随着代码腐败下去,模块之间互相渗透,出现大量隐藏纠结的依赖关系。找到和破除除旧的依赖关系又费时间又费劲。另一方面,保持代码整洁却相对容易。

        所以,解决之道就是保持代码持续整洁和简单。永不让腐坏有机会开始。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/35726.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue中动态绑定样式名的方式有几种?

在Vue中可以使用动态绑定样式名的方式有几种,具体取决于你的需求和使用的场景。 使用对象语法: 可以通过在data中定义一个变量,然后在模板中使用对象语法来动态绑定样式名。 <template><div :class="{ active: isActive }">Hello Vue!</div> &l…

网络文化经营许可证(文网文)办理全面讲解

随着互联网时代的飞速发展&#xff0c;互联网早已渗透到人们的生活中&#xff0c;各类直播、短视频成为大家生活娱乐必不可少的一部分。注册一家从事互联网行业的企业是一个不错的选择。那互联网企业需要办理什么证件资质呢&#xff1f;在互联网行业从事盈利文化活动必须持有网…

【精品方案】智能制造之路(93页PPT)

引言&#xff1a;智能制造之路&#xff1a;革新制造业的引领之旅 随着科技的迅猛发展&#xff0c;特别是人工智能、物联网、大数据等技术的不断进步&#xff0c;制造业正迎来一场深刻的变革。智能制造&#xff0c;作为这场变革的核心&#xff0c;正逐步成为推动产业升级和转型发…

MySQL为什么不建议使用多表JOIN

一、典型回答 之所以不建议使用JOIN查询&#xff0c;最主要的原因就是JOIN的效率比较低。 MySQL是使用了嵌套循环&#xff08;Nested-Loop Join&#xff09;的方式实现关联查询的&#xff0c;简单点说就是要通过两层循环&#xff0c;用第一张表做外循环&#xff0c;第二张表做内…

大模型课程资料-全网最火29套全栈大模型项目实践

29套AI全栈大模型项目实战&#xff0c;人工智能视频课程-多模态大模型&#xff0c;微调技术训练营&#xff0c;大模型多场景实战&#xff0c;AI图像处理&#xff0c;AI量化投资&#xff0c;OPenCV视觉处理&#xff0c;机器学习&#xff0c;Pytorch深度学习&#xff0c;推荐系统…

【LLM】一分钟带你了解Agent工作流四范式

文章目录 1. 大模型直接生成-generation2. 大模型充当工具使用-tool3. 大模型执行思维链-Planning4. 多大模型Agent合作-multiagent collaboration 1. 大模型直接生成-generation 通过提示词&#xff0c;大模型直接生成想要的结果&#xff1a; 2. 大模型充当工具使用-tool …

无人机在农业方面应用的局限性

无人机在农业方面的应用虽然带来了许多便利和效率提升&#xff0c;但也存在一些局限性。以下是对这些局限性的清晰归纳和分点表示&#xff1a; 飞行受限&#xff1a; 无人机在飞行过程中受到一定限制&#xff0c;例如在森林、城市等复杂地形或建筑物密集区域&#xff0c;其空间…

拥抱数字化未来,如何以费控驱动业务发展?

管理费用是企业运营中仅次于人力成本的第二大可控成本&#xff0c;一般会占到企业年度收入的5%—10%&#xff0c;但多数企业存在费用疏于管理、费用管理制度流于纸面难落地、费用浪费严重等问题。 如果不进行科学管理&#xff0c;有专家表示&#xff0c;估计企业每年至少有10%的…

vue总结

1.什么是VUE? Vue就是一套用于构建用户界面的渐进式框架,与其他框架不同的是,Vue被设计为可以自底向上逐渐应用.Vue的核心库只关注图层,不仅容易上手,还便于与第三方库或既有项目整合. 2.Vue的优点 体积小 高效率 双向数据绑定,简化Dom操作 通过MVVM思想实现数据的双向绑定…

Pixea Plus for Mac:图像编辑的极致体验

Pixea Plus for Mac 是一款专为 Mac 用户设计的强大图像编辑软件。凭借其卓越的性能和丰富的功能&#xff0c;它为用户带来了前所未有的图像编辑体验。无论是专业的设计师&#xff0c;还是业余的摄影爱好者&#xff0c;Pixea Plus 都能满足您对于图像编辑的各种需求。 Pixea P…

浏览器扩展V3开发系列之 chrome.cookies 的用法和案例

【作者主页】&#xff1a;小鱼神1024 【擅长领域】&#xff1a;JS逆向、小程序逆向、AST还原、验证码突防、Python开发、浏览器插件开发、React前端开发、NestJS后端开发等等 chrome.cookies API能够让我们在扩展程序中去操作浏览器的cookies。 在使用 chrome.cookies 要先声明…

软考系统架构师考试考点整理就看这一篇

软考系统架构师考试考点整理就看这一篇 最近软考成绩出来了不少同学与笔者沟通&#xff0c;聊到软考现在越来越难了&#xff0c;考了两三次都没过&#xff0c;也有不少新同学咨询软考考试的一些福利政策&#xff0c;投入大量的物力&#xff0c;财力&#xff0c;精力&#xff0c…

如何借助物联网实现土壤监测与保护

如何借助物联网实现土壤监测与保护 高标准农田信息化是指利用现代信息技术&#xff0c;如物联网、大数据、云计算等&#xff0c;对农田进行数字化、智能化的管理&#xff0c;以提高农田的生产效率和可持续发展能力。其中&#xff0c;土壤监测与保护是农田信息化的重要内容之一…

Vue3中根据select得选项值,改变当前元素同级下的子元素得disabled属性值

在 Vue 3 中,你通常不会直接通过类名(或任何其他 DOM 选择器)来获取 DOM 元素,因为 Vue 鼓励你使用数据驱动视图的方式来更新和操作元素。然而,如果你确实需要访问 DOM 元素(这通常是不推荐的,除非有特别的原因),你可以使用 Vue 3 的 ref 或者 refs(在模板中使用 ref…

Python 入门 —— 面向对象编程

Python 入门 —— 面向对象编程 面向对象编程是一种编程范式&#xff0c;通过将对象作为程序的基本单元&#xff0c;每个对象之间可以相互传递信息&#xff0c;并通过各自的方法对信息进行处理&#xff0c;从而达到程序处理的目的。 而面向过程编程则是将程序视为一系列顺序执…

低代码:释放企业创新力的钥匙

近年来&#xff0c;随着信息技术的不断发展&#xff0c;企业对于快速开发应用程序的需求越来越迫切。然而&#xff0c;传统的软件开发过程常常耗时费力&#xff0c;限制了企业的创新潜力。于是&#xff0c;低代码应运而生&#xff0c;成为解决开发难题的一把利器。 低代码开发…

你了解RabbitMQ、RocketMQ和Kafka吗?

是的&#xff0c;我了解 RabbitMQ、RocketMQ 和 Kafka。以下是对这三种消息队列系统的详细介绍&#xff1a; RabbitMQ 概念 RabbitMQ 是一个由 Pivotal 开发的开源消息代理&#xff0c;基于 AMQP&#xff08;Advanced Message Queuing Protocol&#xff09;协议。它支持多种…

智能聊天AI机器人网页怎么聊?这样做很简单

智能聊天AI机器人网页怎么聊&#xff1f;随着科技的飞速发展&#xff0c;智能聊天AI机器人已经逐渐渗透到我们的日常生活中&#xff0c;为我们提供了更加便捷、高效的交流方式。在网页上&#xff0c;这些智能聊天机器人以其独特的魅力&#xff0c;为我们打开了与机器对话的新世…

Epic商店登录时一直转圈圈怎么回事?Epic登录转圈圈解决办法

很多游戏玩家都喜欢在Epic商店上面免费领取游戏&#xff0c;但是经常在登陆领取的过程中&#xff0c;遇到Epic账号登陆不上的问题&#xff0c;登陆界面一直转圈圈&#xff0c;下面分享一下具体解决办法&#xff0c;帮助大家顺利流畅登陆&#xff0c;轻松喜加一。 如果遇到Epic商…

低内阻、高性能数字音频功放芯片-NTP8938

由工采网代理的韩国NF&#xff08;耐福&#xff09;NTP8938是一款支持2X30W低内阻、高性能数字音频功放芯片&#xff1b;采用QFN40封装&#xff0c;芯片内置DSP集成了多功能数字音频信号处理功能&#xff0c;高性能&#xff0c;高保真。 芯片工作电压范围&#xff1a;5V&#x…