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.pswp.cn/diannao/35726.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/35726.shtml
英文地址,請注明出處:http://en.pswp.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…