日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

5个重构原理示例

發(fā)布時(shí)間:2023/12/3 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 5个重构原理示例 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

這篇文章介紹了重構(gòu)真正的開源代碼( Gradle Modules Plugin )時(shí)應(yīng)用的五??種(最著名的)重構(gòu)原理。

語(yǔ)境

當(dāng)我為Gradle Modules Plugin (PR #73 ) 單獨(dú)編譯 module-info.java ,我注意到了一些重構(gòu)的潛力。 結(jié)果,我提交了問題#79 ,后來又通過PR #88 (尚未合并)解決了該問題,在其中重構(gòu)了代碼。

事實(shí)證明,重構(gòu)比我最初想象的要廣泛得多。 在這里,我介紹此PR的一部分,作為我在此處應(yīng)用的重構(gòu)原則的示例。

重構(gòu)原理

注意:這里列出的列表絕不是全面的,并且原則不是原創(chuàng)的(不過,我以自己的聲音并根據(jù)自己的理解提出了這些原則)。 正如我所看到的,這篇文章的最大價(jià)值在于遵循這些原則的真實(shí)示例。

這里介紹的五項(xiàng)原則是:

  • 用“什么”隱藏“如何”
  • 力求一致性
  • 避免深層嵌套
  • 單獨(dú)的關(guān)注點(diǎn)(=單一責(zé)任原則)
  • 明智地避免重復(fù)(=不要重復(fù)自己)
  • 1.用“什么”隱藏“如何”

    該原則只是由Robert Martin提出的“ 干凈代碼”原則的一部分。

    對(duì)我來說,用“什么”隱藏“如何”意味著在任何時(shí)候提取類和方法

    • 我可以識(shí)別出由一段代碼執(zhí)行的獨(dú)特,不平凡的功能,并且
    • 我可以用一個(gè)有意義的名稱將這種不瑣碎的事情隱藏起來。

    示例1:

    重構(gòu)之前,這是RunTaskMutator的一個(gè)片段:

    mainDistribution.contents(copySpec -> copySpec.filesMatching(patchModuleExtension.getJars(), action -> { RelativePath relativePath = action.getRelativePath().getParent().getParent() .append( true , "patchlibs" , action.getName()); action.setRelativePath(relativePath); }));

    這是重構(gòu)后的代碼段:

    mainDistribution.contents( copySpec -> copySpec.filesMatching(patchModuleExtension.getJars(), this ::updateRelativePath) );

    綜上所述,我們:

    • 隱藏如何更新相對(duì)路徑
    • 與我們有什么 (=我們更新它的事實(shí))。

    由于有了這樣的重構(gòu),掌握mainDistribution發(fā)生的事情要容易mainDistribution 。

    作為參考, 這里提供了updateRelativePath的內(nèi)容。

    示例2:

    這是重構(gòu)之前TestTask類的一部分的樣子:

    TestEngine.select(project).ifPresent(testEngine -> { args.addAll(List.of( "--add-reads" , moduleName + "=" + testEngine.moduleName)); Set<File> testDirs = testSourceSet.getOutput().getClassesDirs().getFiles(); getPackages(testDirs).forEach(p -> { args.add( "--add-opens" ); args.add(String.format( "%s/%s=%s" , moduleName, p, testEngine.addOpens)); }); });

    如下所示:

    TestEngine.select(project).ifPresent(testEngine -> Stream.concat( buildAddReadsStream(testEngine), buildAddOpensStream(testEngine) ).forEach(jvmArgs::add));

    同樣,我們:

    • 隱藏如何 --add-reads和--add-opens選項(xiàng)的值
    • 與我們有什么 (=我們指定它們的事實(shí))。

    作為參考,可在此處獲得buildAddReadsStream和buildAddOpensStream的內(nèi)容。

    2.追求一致性

    這很籠統(tǒng),但是我的意思是我們可以獲得任何合理的一致性。

    例如, 唐納德·拉布 ( Donald Raab ) 關(guān)于對(duì)稱的博客文章就是努力保持一致性的一個(gè)很好的例子。 不用說,我完全同意他的結(jié)論:

    具有對(duì)稱性的大型系統(tǒng)變得更容易理解,因?yàn)槟梢詸z測(cè)并期望重復(fù)出現(xiàn)的模式。

    Donald Raab,對(duì)稱的同情

    對(duì)于Gradle Modules Plugin,這主要?dú)w結(jié)為提取AbstractModulePluginTask基類并統(tǒng)一任務(wù)查找和配置調(diào)度過程。

    例如,重構(gòu)之前的JavadocTask和TestTask是:

    public class JavadocTask { public void configureJavaDoc(Project project) { Javadoc javadoc = (Javadoc) project.getTasks().findByName(JavaPlugin.JAVADOC_TASK_NAME); if (javadoc != null ) { // ... } } } public class TestTask { public void configureTestJava(Project project, String moduleName) { Test testJava = (Test) project.getTasks().findByName(JavaPlugin.TEST_TASK_NAME); // ... (no null check) } }

    之后,它們是:

    public class JavadocTask extends AbstractModulePluginTask { public void configureJavaDoc() { helper().findTask(JavaPlugin.JAVADOC_TASK_NAME, Javadoc. class ) .ifPresent( this ::configureJavaDoc); } private void configureJavaDoc(Javadoc javadoc) { /* ... */ } } public class TestTask extends AbstractModulePluginTask { public void configureTestJava() { helper().findTask(JavaPlugin.TEST_TASK_NAME, Test. class ) .ifPresent( this ::configureTestJava); } private void configureTestJava(Test testJava) { /* ... */ } }

    供參考: JavaDocTask diff和TestTask diff 。

    3.避免深度嵌套

    我想這很明顯。 對(duì)我而言,控制結(jié)構(gòu)的深層嵌套非常難以閱讀和掌握。

    結(jié)果,我重構(gòu)了以下getPackages方法:

    private static Set<String> getPackages(Collection<File> dirs) { Set<String> packages = new TreeSet<>(); for (File dir : dirs) { if (dir.isDirectory()) { Path dirPath = dir.toPath(); try (Stream<Path> entries = Files.walk(dirPath)) { entries.forEach(entry -> { if (entry.toFile().isFile()) { String path = entry.toString(); if (isValidClassFileReference(path)) { Path relPath = dirPath.relativize(entry.getParent()); packages.add(relPath.toString().replace(File.separatorChar, '.' )); } } }); } catch (IOException e) { throw new GradleException( "Failed to scan " + dir, e); } } } return packages; }

    如下所示:

    private static Set<String> getPackages(Collection<File> dirs) { return dirs.stream() .map(File::toPath) .filter(Files::isDirectory) .flatMap(TestTask::buildRelativePathStream) .map(relPath -> relPath.toString().replace(File.separatorChar, '.' )) .collect(Collectors.toCollection(TreeSet:: new )); } private static Stream<Path> buildRelativePathStream(Path dir) { try { return Files.walk(dir) .filter(Files::isRegularFile) .filter(path -> isValidClassFileReference(path.toString())) .map(path -> dir.relativize(path.getParent())); } catch (IOException e) { throw new GradleException( "Failed to scan " + dir, e); } }

    可在此處找到完整的差異 。

    4.單獨(dú)的問題

    SRP( 單一職責(zé)原則 )是眾所周知的軟件設(shè)計(jì)原則。 在這里,我們可以看到其在從RunTaskMutator中提取StartScriptsMutator應(yīng)用程序。

    之前:

    public class RunTaskMutator { // common fields public void configureRun() { /* ... */ } public void updateStartScriptsTask(String taskStartScriptsName) { /* ... */ } // 12 other methods (incl. 2 common methods) }

    后:

    public class RunTaskMutator extends AbstractExecutionMutator { public void configureRun() { /* ... */ } ??// 2 other methods } public class StartScriptsMutator extends AbstractExecutionMutator { public void updateStartScriptsTask(String taskStartScriptsName) { /* ... */ } // 8 other methods }

    由于提取了StartScriptsMutator ,因此更容易理解以下范圍:

    • 本身配置run任務(wù),
    • 配置相關(guān)的startScripts任務(wù)。

    供參考:以上提取的提交 。

    5.明智地避免重復(fù)

    DRY( 請(qǐng)勿重復(fù) )是另一種著名的軟件開發(fā)原理。 但是,以我的經(jīng)驗(yàn),這個(gè)原則有時(shí)太過復(fù)雜,導(dǎo)致代碼無法重復(fù),但是也太復(fù)雜了。

    換句話說,只有在成本/收益比為正時(shí),才應(yīng)該重復(fù)數(shù)據(jù)刪除:

    • 成本 :重構(gòu)時(shí)間,產(chǎn)生的復(fù)雜性等
    • 獲得 :沒有重復(fù)(或更嚴(yán)格地說,是唯一的真理來源 )。

    Gradle Modules Plugin中的一個(gè)這樣的例子(在我看來,成本/收益比接近零,但仍然為正)是PatchModuleResolver的引入。

    下面是重構(gòu)前的代碼片段,其中包括:

  • PatchModuleExtension.configure方法。
  • 使用它的地方( TestTask )。
  • 無法使用的地方( RunTaskMutator )。
  • 不能使用它的另一個(gè)地方( JavadocTask )。
  • // 1. PatchModuleExtension public List<String> configure(FileCollection classpath) { List<String> args = new ArrayList<>(); config.forEach(patch -> { String[] split = patch.split( "=" ); String asPath = classpath.filter(jar -> jar.getName().endsWith(split[ 1 ])).getAsPath(); if (asPath.length() > 0 ) { args.add( "--patch-module" ); args.add(split[ 0 ] + "=" + asPath); } } ); return args; } // 2. TestTask args.addAll(patchModuleExtension.configure(testJava.getClasspath())); // 3. RunTaskMutator patchModuleExtension.getConfig().forEach(patch -> { String[] split = patch.split( "=" ); jvmArgs.add( "--patch-module" ); jvmArgs.add(split[ 0 ] + "=" + PATCH_LIBS_PLACEHOLDER + "/" + split[ 1 ]); } ); // 4. JavadocTask patchModuleExtension.getConfig().forEach(patch -> { String[] split = patch.split( "=" ); String asPath = javadoc.getClasspath().filter(jar -> jar.getName().endsWith(split[ 1 ])).getAsPath(); if (asPath != null && asPath.length() > 0 ) { options.addStringOption( "-patch-module" , split[ 0 ] + "=" + asPath); } } );

    引入PatchModuleResolver ,代碼如下所示:

    // 1. PatchModuleExtension public PatchModuleResolver resolve(FileCollection classpath) { return resolve(jarName -> classpath.filter(jar -> jar.getName().endsWith(jarName)).getAsPath()); } public PatchModuleResolver resolve(UnaryOperator<String> jarNameResolver) { return new PatchModuleResolver( this , jarNameResolver); } // 2. TestTask patchModuleExtension.resolve(testJava.getClasspath()).toArgumentStream().forEach(jvmArgs::add); // 3. RunTaskMutator patchModuleExtension.resolve(jarName -> PATCH_LIBS_PLACEHOLDER + "/" + jarName).toArgumentStream().forEach(jvmArgs::add); // 4. JavadocTask patchModuleExtension.resolve(javadoc.getClasspath()).toValueStream() .forEach(value -> options.addStringOption( "-patch-module" , value));

    多虧了重構(gòu),現(xiàn)在只有一個(gè)地方( PatchModuleResolver ),我們?cè)谄渲蟹指盍薖atchModuleExtension類的config條目。

    供參考:DIFFS 1 , 2 , 3 , 4 。

    摘要

    在這篇文章中,我介紹了以下五個(gè)重構(gòu)原則:

  • 用“什么”隱藏“如何”
  • 力求一致性
  • 避免深層嵌套
  • 單獨(dú)關(guān)注
  • 明智地避免重復(fù)
  • 每個(gè)原則都附有一個(gè)真實(shí)的示例,希望該示例顯示了遵循該原則如何產(chǎn)生簡(jiǎn)潔的代碼。

    翻譯自: https://www.javacodegeeks.com/2019/05/5-refactoring-principles-example.html

    總結(jié)

    以上是生活随笔為你收集整理的5个重构原理示例的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。