├── .gitattributes ├── .gitignore ├── README.md ├── RefactorPractice.UnitTest ├── Ch1LabTests │ └── RentalTests.cs ├── Properties │ └── AssemblyInfo.cs ├── RefactorPractice.UnitTest.csproj └── packages.config └── RefactorPractice ├── App.config ├── BigRefactoring ├── ConvertProceduralDesignToObjects │ └── README.md ├── ExtractHierarchy │ └── README.md ├── SeparateDomainFromPresentation │ └── README.md └── TeaseApartInheritance │ └── README.md ├── Ch1Lab ├── ChildrensPrice.cs ├── Customer.cs ├── Movie.cs ├── NewReleasePrice.cs ├── Price.cs ├── RegularPrice.cs └── Rental.cs ├── ComposingMethods ├── ExtractMethod │ ├── ExtractMethod.cs │ └── README.md ├── InlineMethod │ ├── InlineMethod.cs │ └── README.md ├── InlineTemp │ ├── InlineTemp.cs │ └── README.md ├── IntroduceExplainingVariable │ ├── IntroduceExplainingVariable.cs │ └── README.md ├── RemoveAssignmentsToParameters │ ├── README.md │ └── RemoveAssignentToParameters.cs ├── ReplaceMethodWithMethodObject │ ├── README.md │ └── ReplaceMethodWithMethodObject.cs ├── ReplaceTempWithQuery │ ├── README.md │ └── ReplaceTempWithQuery.cs ├── SplitTemporaryVariable │ ├── README.md │ └── SplitTemporaryVariable.cs └── SubstituteAlgorithm │ ├── README.md │ └── SubstituteAlgorithm.cs ├── DealWithGeneralization ├── CollapseHierarchy │ └── README.md ├── ExtractInterface │ ├── ExtractInterface.cs │ └── README.md ├── ExtractSubClass │ ├── ExtractSubClass.cs │ └── README.md ├── ExtractSuperClass │ ├── ExtractSuperClass.cs │ └── README.md ├── FormTemplateMethod │ ├── FormTemplateMethod.cs │ └── README.md ├── PullUpConstructorBody │ ├── PullUpConstructorBody.cs │ └── README.md ├── PullUpField │ └── README.md ├── PullUpMethod │ └── README.md ├── PushDownField │ └── README.md ├── PushDownMethod │ └── README.md ├── ReplaceDelegationWithInheritance │ ├── README.md │ └── ReplaceDelegationWithInheritance.cs └── ReplaceInheritanceWithDelegation │ ├── README.md │ └── ReplaceInheritanceWithDelegation.cs ├── MakingMethodCallsSimpler ├── AddParameter │ └── README.md ├── EncapsulateDowncast │ ├── EncapsulateDowncast.cs │ └── README.md ├── HideMethod │ └── README.md ├── IntroduceParameterObject │ ├── IntroduceParameterObject.cs │ └── README.md ├── ParameterizeMethod │ ├── ParameterizeMethod.cs │ └── README.md ├── PreserveWholeObject │ ├── PreserveWholeObject.cs │ └── README.md ├── RemoveParameter │ └── README.md ├── RemoveSettingMethod │ ├── README.md │ └── RemoveSettingMethod.cs ├── RenameMethod │ ├── README.md │ └── RenameMethod.cs ├── ReplaceConstructorWithFactoryMethod │ ├── README.md │ └── ReplaceConstructorWithFactoryMethod.cs ├── ReplaceErrorCodeWithException │ ├── README.md │ └── ReplaceErrorCodeWithException.cs ├── ReplaceExceptionWithTest │ └── README.md ├── ReplaceParameterWithExplicitMethod │ ├── README.md │ └── ReplaceParameterWithExplicitMethod.cs ├── ReplaceParameterWithMethods │ ├── README.md │ └── ReplaceParameterWithMethods.cs └── SeparateQueryFromModifier │ ├── README.md │ └── SeparateQueryFromModifier.cs ├── MovingFeaturesBetweenObjects ├── ExtractClass │ ├── ExtractClass.cs │ └── README.md ├── HideDelegate │ ├── HideDelegate.cs │ └── README.md ├── InlineClass │ ├── InlineClass.cs │ └── README.md ├── IntroduceForeignMethod │ ├── IntroduceForeignMethod.cs │ └── README.md ├── IntroduceLocalExtension │ └── README.md ├── MoveField │ ├── MoveField.cs │ ├── MoveFieldWithSelfEncapsulation.cs │ └── README.md ├── MoveMethod │ ├── MoveMethod.cs │ └── README.md └── RemoveMiddleMan │ ├── README.md │ └── RemoveMiddleMan.cs ├── OrganizingData ├── ChangeBidirectonalAssociationToUnidirectional │ ├── ChangeBidirectonalAssociationToUnidirectional.cs │ └── README.md ├── ChangeReferenceToValue │ ├── ChangeReferenceToValue.cs │ └── README.md ├── ChangeUnidirectionalAssociationToBidirectonal │ ├── ChangeUnidirectionalAssociationToBidirectonal.cs │ └── README.md ├── ChangeValueToReference │ ├── ChangeValueToReference.cs │ └── README.md ├── DuplicateObservedData │ └── README.md ├── EncapsulateCollection │ └── README.md ├── EncapsulateField │ ├── EncapsulateField.cs │ └── README.md ├── ReplaceArrayWithObject │ ├── README.md │ └── ReplaceArrayWithObject.cs ├── ReplaceDataValueWithObject │ ├── README.md │ └── ReplaceDataValueWithObject.cs ├── ReplaceMagicNumberWithSymbolicConstant │ ├── README.md │ └── ReplaceMagicNumberWithSymbolicConstant.cs ├── ReplaceRecordWithDataClass │ └── README.md ├── ReplaceSubclassWithFields │ ├── README.md │ └── ReplaceSubclassWithFields.cs ├── ReplaceTypeCodeWithClass │ ├── README.md │ └── ReplaceTypeCodeWithClass.cs ├── ReplaceTypeCodeWithStateOrStrategy │ ├── README.md │ └── ReplaceTypeCodeWithStateOrStrategy.cs ├── ReplaceTypeCodeWithSubclasses │ ├── README.md │ └── ReplaceTypeCodeWithSubclasses.cs └── SelfEncapsulateField │ ├── README.md │ └── SelfEncapsulateField.cs ├── Program.cs ├── Properties └── AssemblyInfo.cs ├── RefactorPractice.csproj ├── RefactorPractice.sln └── SimplifyingConditionalExpressions ├── ConsolidateConditionalExpression ├── ConsolidateConditionalExpression.cs └── README.md ├── ConsolidateDuplicateConditionalFragments ├── ConsolidateDuplicateConditionalFragments.cs └── README.md ├── DecomposeConditional ├── DecomposeConditional.cs └── README.md ├── IntroduceAssertion └── README.md ├── IntroduceNullObject ├── IntroduceNullObject.cs └── README.md ├── RemoveControlFlag ├── README.md └── RemoveControlFlag.cs ├── ReplaceConditionalWithPolymorphism ├── README.md └── ReplaceConditionalWithPolymorphism.cs └── ReplaceNestedConditionalWithGuardClauses ├── README.md └── ReplaceNestedConditionalWithGuardClauses.cs /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RefactorPractice 2 | 重構-改善既有程式的設計Lab 3 | 4 | 使用C#改寫書上的範例 5 | 6 | ## 重構的定義: 7 | 不改變外在的行為的前提下,對程式碼做出修正,以改進程式的內部結構。本質上來說,重構就是在`程式碼寫好之後改進它的設計` 8 | 9 | ## 重構的原則: 10 | * 需要有穩定且堅固的測試機制 11 | * 以微小步伐修改程式,如果引入錯誤便可以很容易發現 12 | * 如果覺得比較困難增加新的功能,就先重構後再增加 13 | * 只有寫出人類容易理解的程式碼,才是優秀的程式員 14 | 15 | ## 兩頂帽子 - 重構與增加新功能 16 | * 增加新功能:不應該修改既有程式碼,只管增加新功能以通過測試 17 | * 重構:不能增加新功能,只管修改程式結構。只在絕對必要的時刻才修改測試。 18 | 19 | ## 為何要重構? 20 | * 改進軟體設計:一個主要的方向就是`消除重複的程式碼`。 21 | * 使軟體更容易被理解:提高可讀性。 22 | * 幫你找到Bug 23 | * 幫你提高編程速度 24 | 25 | ## 何時重構? 26 | * 三次法則:事不過三,三則重構 27 | * 增加功能的時候重構 28 | * 修改錯誤的時候重構 29 | * Code Review的時候重構 30 | 31 | ## 程式碼的壞味道(Bad Smell) 32 | * 重複的程式碼(Duplicated Code) 33 | * 過長的函式(Long Method) 34 | * 過大的類別(Large Class) 35 | * 過長的參數列(Large Parameter List) 36 | * 發散式變化(Divergent Change) 37 | * 散彈式修改(Shotgun Surgery) 38 | * 依戀情節(Feature Envy) 39 | * 資料泥團(Data Clump) 40 | * 基本型別偏執(Primitive Obsession) 41 | * Switch case 42 | * 平行繼承體系(Parallel Inheritance Hierarchies) 43 | * 冗員類別(Lazy Class) 44 | * 誇大其談未來性(Speculative Generality) 45 | * 暫時欄位(Temporary Field) 46 | * 過度耦合的訊息鏈(Message Chains) 47 | * 中間轉手人(Middle Man) 48 | * 狎暱關係(inappropriate Intimacy) 49 | * 異曲同工的類別(Alternative Classes With Different Interfaces) 50 | * 不完美的程式庫類別(Incomplete Library Class) 51 | * 純資料類別(Data Class) 52 | * 被拒絕的遺贈(Refused Bequest) 53 | * 過多的註釋(Comments) 54 | 55 | ## 重構手法 56 | 57 | ### 重新組織函式(Composing Methods) 58 | * [提煉函式(Extract Method)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/ComposingMethods/ExtractMethod) 59 | * [行內函式(Inline Method)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/ComposingMethods/InlineMethod) 60 | * [行內暫時變數(Inline Temp)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/ComposingMethods/InlineTemp) 61 | * [用回傳函式替換暫時變數(Replace Temp With Query)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/ComposingMethods/ReplaceTempWithQuery) 62 | * [以變數解釋運算式用途(Introduce Explaining Variable)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/ComposingMethods/IntroduceExplainingVariable) 63 | * [剖解暫時變數(Split Temporary Variable)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/ComposingMethods/SplitTemporaryVariable) 64 | * [移除對參數賦值(Remove Assignments To Parameters)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/ComposingMethods/RemoveAssignmentsToParameters) 65 | * [用函式物件替換函式(Replace Method With MethodObject)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/ComposingMethods/ReplaceMethodWithMethodObject) 66 | * [替換演算法(Substitute Algorithm)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/ComposingMethods/SubstituteAlgorithm) 67 | 68 | ### 在物件中間搬移特性(Moving Features Between Objects) 69 | * [移動函式(Move Method)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/MovingFeaturesBetweenObjects/MoveMethod) 70 | * [移動欄位(Move Field)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/MovingFeaturesBetweenObjects/MoveField) 71 | * [提煉類別(Move Class)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/MovingFeaturesBetweenObjects/ExtractClass) 72 | * [行內類別(Inline Class)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/MovingFeaturesBetweenObjects/InlineClass) 73 | * [隱藏委託關係(Hide Delegate)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/MovingFeaturesBetweenObjects/HideDelegate) 74 | * [移除中間人(Remove Middle Man)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/MovingFeaturesBetweenObjects/RemoveMiddleMan) 75 | * [引入外加函式(Introduce Foreign Method)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/MovingFeaturesBetweenObjects/IntroduceForeignMethod) 76 | * [引入區域性擴展(Introduce Local Extension)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/MovingFeaturesBetweenObjects/IntroduceLocalExtension) 77 | 78 | ### 重新組織資料(Organizing Data) 79 | * [自我封裝欄位(Self Encapsulate Field)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/OrganizingData/SelfEncapsulateField) 80 | * [以物件取代資料值(Replace Data Value With Object)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/OrganizingData/ReplaceDataValueWithObject) 81 | * [將實值物件改為參考物件(Change Value To Reference)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/OrganizingData/ChangeValueToReference) 82 | * [將參考物件改為實值物件(Change Reference To Value)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/OrganizingData/ChangeReferenceToValue) 83 | * [以物件取代陣列(Replace Array With Object)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/OrganizingData/ReplaceArrayWithObject) 84 | * [複製被監視的資料(Duplicate Observed Data)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/OrganizingData/DuplicateObservedData) 85 | * [將單向關聯改為雙向(Change Unidirectional Association To Bidirectonal)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/OrganizingData/ChangeUnidirectionalAssociationToBidirectonal) 86 | * [將雙向關聯改為單向(Change Bidirectonal Association To Unidirectional)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/OrganizingData/ChangeBidirectonalAssociationToUnidirectional) 87 | * [以符號常數(字面常數)取代魔術數字 (Replace Magic Number With Symbolic Constant)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/OrganizingData/ReplaceMagicNumberWithSymbolicConstant) 88 | * [封裝欄位(Encapsulate Field)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/OrganizingData/EncapsulateField) 89 | * [封裝群集(Encapsulate Collection)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/OrganizingData/EncapsulateCollection) 90 | * [以資料類別取代紀錄(Replace Record With Data Class)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/OrganizingData/ReplaceRecordWithDataClass) 91 | * [以類別取代型別代碼(Replace TypeCode With Class)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/OrganizingData/ReplaceTypeCodeWithClass) 92 | * [以子類別取代型別代碼(Replace Type Code With Subclasses)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/OrganizingData/ReplaceTypeCodeWithSubclasses) 93 | * [以State/Strategy取代型別代碼(Replace Type Code With State/Strategy)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/OrganizingData/ReplaceTypeCodeWithStateOrStrategy) 94 | * [以欄位取代子類別(Replace Subclass With Fields)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/OrganizingData/ReplaceSubclassWithFields) 95 | 96 | ### 簡化條件式(Simplifying Conditional Expressions) 97 | * [分解條件式(Decompose Conditional)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/SimplifyingConditionalExpressions/DecomposeConditional) 98 | * [合併條件式(Consolidate Conditional Expression)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/SimplifyingConditionalExpressions/ConsolidateConditionalExpression) 99 | * [合併重複的條件片段(Consolidate Duplicate Conditional Fragments)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/SimplifyingConditionalExpressions/ConsolidateDuplicateConditionalFragments) 100 | * [移除控制旗標(Remove Control Flag)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/SimplifyingConditionalExpressions/RemoveControlFlag) 101 | * [以衛述句取代巢狀條件式(Replace Nested Conditional With Guard Clauses)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/SimplifyingConditionalExpressions/ReplaceNestedConditionalWithGuardClauses) 102 | * [以多型取代條件式(Replace Conditional With Polymorphism)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/SimplifyingConditionalExpressions/ReplaceConditionalWithPolymorphism) 103 | * [引入Null Object(Introduce NullObject)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/SimplifyingConditionalExpressions/IntroduceNullObject) 104 | * [引入斷言(Introduce Assertion)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/SimplifyingConditionalExpressions/IntroduceAssertion) 105 | 106 | ### 簡化函式呼叫(Making Method Calls Simpler) 107 | * [重新命名函式(Rename Method)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/MakingMethodCallsSimpler/RenameMethod) 108 | * [添加參數(Add Parameter)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/MakingMethodCallsSimpler/AddParameter) 109 | * [移除參數(Remove Parameter)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/MakingMethodCallsSimpler/RemoveParameter) 110 | * [將查詢函式和修改函式分離(Separate Query From Modifier)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/MakingMethodCallsSimpler/SeparateQueryFromModifier) 111 | * [令函式攜帶參數(Parameterize Method)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/MakingMethodCallsSimpler/ParameterizeMethod) 112 | * [以明確函式取代參數(Replace Parameter With Explicit Method)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/MakingMethodCallsSimpler/ReplaceParameterWithExplicitMethod) 113 | * [保持物件完整(Preserve Whole Object)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/MakingMethodCallsSimpler/PreserveWholeObject) 114 | * [以函式取代參數(Replace Parameter With Methods)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/MakingMethodCallsSimpler/ReplaceParameterWithMethods) 115 | * [引入參數物件(Introduce Parameter Objects)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/MakingMethodCallsSimpler/IntroduceParameterObject) 116 | * [移除設值函式(Remove Setting Method)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/MakingMethodCallsSimpler/RemoveSettingMethod) 117 | * [隱藏某個函式(Hide Method)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/MakingMethodCallsSimpler/HideMethod) 118 | * [以「工廠函式」取代「建構式」(Replace Constructor With Factory Method)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/MakingMethodCallsSimpler/ReplaceConstructorWithFactoryMethod) 119 | * [封裝「向下轉型」動作 (Encapsulate Downcast)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/MakingMethodCallsSimpler/EncapsulateDowncast) 120 | * [以異常取代錯誤碼(Replace Error Code With Exception)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/MakingMethodCallsSimpler/ReplaceErrorCodeWithException) 121 | * [以測試取代異常(Replace Exception With Test)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/MakingMethodCallsSimpler/ReplaceExceptionWithTest) 122 | 123 | ### 處理概括關係(Deal With Generalization) 124 | * [欄位上移(Pull Up Field)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/DealWithGeneralization/PullUpField) 125 | * [函式上移(Pull Up Method)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/DealWithGeneralization/PullUpMethod) 126 | * [建構式本體上移(Pull Up Constrctor Body)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/DealWithGeneralization/PullUpConstrctorBody) 127 | * [函式下移(Push Down Method)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/DealWithGeneralization/PushDownMethod) 128 | * [欄位下移(Push Down Field)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/DealWithGeneralization/PushDownField) 129 | * [提煉子類別(Extract Subclass)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/DealWithGeneralization/ExtractSubClass) 130 | * [提煉超類別(Extract Superclass)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/DealWithGeneralization/ExtractSuperclass) 131 | * [提煉介面(Extract Interface)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/DealWithGeneralization/ExtractInterface) 132 | * [摺疊繼承關係(Collapse Hierarchy)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/DealWithGeneralization/CollapseHierarchy) 133 | * [塑造模板函式(Form Template Method)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/DealWithGeneralization/FormTemplateMethod) 134 | * [以委託取代繼承 (Replace Inheritance With Delegation)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/DealWithGeneralization/ReplaceInheritanceWithDelegation) 135 | * [以繼承取代委託(Replace Delegation With Inheritance)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/DealWithGeneralization/ReplaceDelegationWithInheritance) 136 | 137 | ### 大型重構(Big Refactoring) 138 | * [以繼承取代委託(Replace Delegation With Inheritance)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/BigRefactoring/TeaseApartInheritance) 139 | * [將程序式設計轉化為物件設計(Convert Procedural Design To Objects)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/BigRefactoring/ConvertProceduralDesignToObjects) 140 | * [將領域和表述/顯示分離(Separate Domain From Presentation)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/BigRefactoring/SeparateDomainFromPresentation) 141 | * [提煉繼承體系(Extract Hierarchy)](https://github.com/BryanYu/RefactorPractice/tree/master/RefactorPractice/BigRefactoring/ExtractHierarchy) 142 | 143 | ## 重構的現實 144 | 145 | ### 為什麼開發者不願意重構他們程式 146 | * 不知道如何重構 147 | * 如果這些利益是需要長時間才能展現的,何必現在付出努力? 長遠看來,當專案收穫這些利益的時候,或許自己已經不在職位上了 148 | * 程式碼重構是額外的工作,老闆付錢給你,是要你增加新功能 149 | * 重構可能破壞現有程式 150 | 151 | ### 如何重構,在哪裡重構 152 | * 使用自動化工具來識別哪裡需要重構,以及提供重構的建議 153 | 154 | ### 重構以求短期利益 155 | * 重構帶來的短期利益是讓軟體更好修改、更易維護 156 | * 長期的願景是程式員或程式開發團隊如何開發並維護自己的軟體 157 | 158 | ### 降低重構帶來的額外開銷 159 | * 目前有一些工具和技術,可以使重構快速而相對無痛的完成 160 | * 重構雖然需要額外開銷,但可以從在程式開發的其他階段協助降低所需心力即滯怠時間獲得補償 161 | * 儘管重構開銷太大,但當他成為開發規則的一部分,就不會覺得他費事,反而會覺得是不可或缺。 162 | 163 | ### 安全的進行重構 164 | * 相信自己的編碼能力 165 | * 相信編譯器會捕捉遺漏的錯誤 166 | * 相信測試套件能捕捉你和編譯器都遺漏的錯誤 167 | * 相信程式碼複審(code review)能捕捉你、編譯器、測試套件都遺漏的錯誤 168 | 169 | ### 安全重構的侷限性 170 | * 程式員有可能犯錯 171 | * 有編譯器無法捕捉的錯誤,特別是與繼承相關的作用域錯誤 172 | * 無法保證測試套件涵蓋所有可能情況 173 | * 程式碼複審人員可能無法徹底檢查別人的程式碼 174 | 175 | ### 學習重構的技術 176 | * 隨時挑一個目標:某個程式碼開始發臭,就應該將問題解決掉。你應該朝目標前進,達成後就停止。 177 | * 沒把握就停下來:你無法證明自己所做的一切還能夠保持程式的原意,就停下來,有改善的成果就發布,沒有的話就撤銷。 178 | * 學習原路返回:當重構已經失控時,要果斷放棄,回到上一個測試可以通過的程式碼版本。 179 | * 二重奏:兩人結對重構,你的搭檔會看到與想到你沒發現的東西,當你的夥伴不知道你在重構什麼東西,通常自己也會不知道在重構什麼。在重構之前先與夥伴討論目標與方向,這樣夥伴才能指出你的錯誤。 180 | * -------------------------------------------------------------------------------- /RefactorPractice.UnitTest/Ch1LabTests/RentalTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using NUnit.Framework; 7 | using RefatorPractice.Ch1Lab; 8 | 9 | namespace RefactorPractice.UnitTest.Ch1LabTests 10 | { 11 | [TestFixture] 12 | public class RentalTests 13 | { 14 | [Test] 15 | public void When_Customer_Add_Rental_Get_Statement() 16 | { 17 | var customer = new Customer("Bryan"); 18 | var rental = new Rental(new Movie("Movie1", 0), 2); 19 | customer.AddRental(rental); 20 | var actual = customer.GetStatement(); 21 | var expected = @"Rental Record forBryan Movie1 2 Amount owed is 2 You earned 1 frequent renter points"; 22 | StringAssert.AreEqualIgnoringCase(expected, actual); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /RefactorPractice.UnitTest/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("RefactorPractice.UnitTest")] 6 | [assembly: AssemblyDescription("")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany("")] 9 | [assembly: AssemblyProduct("RefactorPractice.UnitTest")] 10 | [assembly: AssemblyCopyright("Copyright © 2017")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | 14 | [assembly: ComVisible(false)] 15 | 16 | [assembly: Guid("65544821-9a45-4209-900d-ce53136b51d8")] 17 | 18 | // [assembly: AssemblyVersion("1.0.*")] 19 | [assembly: AssemblyVersion("1.0.0.0")] 20 | [assembly: AssemblyFileVersion("1.0.0.0")] 21 | -------------------------------------------------------------------------------- /RefactorPractice.UnitTest/RefactorPractice.UnitTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {65544821-9A45-4209-900D-CE53136B51D8} 8 | Library 9 | Properties 10 | RefactorPractice.UnitTest 11 | RefactorPractice.UnitTest 12 | v4.5.2 13 | 512 14 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15 | 15.0 16 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 17 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 18 | False 19 | UnitTest 20 | 21 | 22 | 23 | 24 | true 25 | full 26 | false 27 | bin\Debug\ 28 | DEBUG;TRACE 29 | prompt 30 | 4 31 | 32 | 33 | pdbonly 34 | true 35 | bin\Release\ 36 | TRACE 37 | prompt 38 | 4 39 | 40 | 41 | 42 | ..\RefactorPractice\packages\NUnit.3.9.0\lib\net45\nunit.framework.dll 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | {DC520388-FBCD-4A58-98BC-EBC9BEDA2588} 57 | RefactorPractice 58 | 59 | 60 | 61 | 62 | 63 | 64 | 此專案參考這部電腦上所缺少的 NuGet 封裝。請啟用 NuGet 封裝還原,以下載該封裝。如需詳細資訊,請參閱 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的檔案是 {0}。 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /RefactorPractice.UnitTest/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /RefactorPractice/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /RefactorPractice/BigRefactoring/ConvertProceduralDesignToObjects/README.md: -------------------------------------------------------------------------------- 1 | ## 將程序式設計轉化為物件設計(Convert Procedural Design To Objects) 2 | 3 | 有一些程式碼以程序式風格撰寫。 4 | 5 | 將資料紀錄變成物件,將行為分開,並將行為移入相關物件中。 6 | 7 | ## 優點: 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /RefactorPractice/BigRefactoring/ExtractHierarchy/README.md: -------------------------------------------------------------------------------- 1 | ## 提煉繼承體系(Extract Hierarchy) 2 | 3 | 某個class做了過多的工作,其中一部分是以大量條件式完成的 4 | 5 | 建立繼承體系,以一個subclass表示一種特殊情況 6 | 7 | ## 優點: 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /RefactorPractice/BigRefactoring/SeparateDomainFromPresentation/README.md: -------------------------------------------------------------------------------- 1 | ## 將領域和表述/顯示分離(Separate Domain From Presentation) 2 | 3 | 某些GUI class之中包含了domain logic 4 | 5 | 將domain logic分離出來,為他們建立獨立的domain classes 6 | 7 | ## 優點: 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /RefactorPractice/BigRefactoring/TeaseApartInheritance/README.md: -------------------------------------------------------------------------------- 1 | ## 梳理並分解繼承體系(Tease Apart Inheritance) 2 | 3 | 某個繼承體系同時承擔兩個責任 4 | 5 | 建立兩個繼承體系,並透過委託關係讓其中一個可以呼叫另一個 6 | 7 | ## 優點: 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /RefactorPractice/Ch1Lab/ChildrensPrice.cs: -------------------------------------------------------------------------------- 1 | namespace RefatorPractice.Ch1Lab 2 | { 3 | public class ChildrensPrice : Price 4 | { 5 | public override int GetPriceCode() 6 | { 7 | return Movie.Childrens; 8 | } 9 | 10 | public override double GetCharge(int daysRented) 11 | { 12 | var result = 1.5; 13 | if (daysRented > 3) 14 | { 15 | result += (daysRented - 3) * 1.5; 16 | } 17 | return result; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /RefactorPractice/Ch1Lab/Customer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Xml; 5 | 6 | namespace RefatorPractice.Ch1Lab 7 | { 8 | public class Customer 9 | { 10 | private List _rental = new List(); 11 | 12 | public string Name { get; } 13 | 14 | public Customer(string name) 15 | { 16 | this.Name = name; 17 | } 18 | 19 | public void AddRental(Rental rental) 20 | { 21 | this._rental.Add(rental); 22 | } 23 | 24 | public string GetStatement() 25 | { 26 | var result = "Rental Record for" + this.Name; 27 | 28 | this._rental.ForEach( 29 | item => 30 | { 31 | result += $" {item.Movie.Title} {item.GetCharge()}"; 32 | }); 33 | result += $" Amount owed is {this.GetTotalCharege()} "; 34 | result += $"You earned {this.GetTotalFrequentRenterPoints()} frequent renter points"; 35 | 36 | return result; 37 | } 38 | 39 | public string GetHtmlStatement() 40 | { 41 | var result = $@"

Rentals for {this.Name}

\n"; 42 | this._rental.ForEach( 43 | item => 44 | { 45 | result += $@"{item.Movie.Title}: {item.GetCharge()}"; 46 | }); 47 | result += $@"

You owe{this.GetTotalCharege()}

\n"; 48 | result += 49 | $@"On this rental you earned {this.GetTotalFrequentRenterPoints()} frequent renter points"; 50 | return result; 51 | } 52 | 53 | private double GetTotalCharege() 54 | { 55 | double result = this._rental.Sum(item => item.GetCharge()); 56 | return result; 57 | } 58 | 59 | private int GetTotalFrequentRenterPoints() 60 | { 61 | int result = this._rental.Sum(item => item.GetFrequentRenterPoints()); 62 | return result; 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /RefactorPractice/Ch1Lab/Movie.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Net.Http.Headers; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace RefatorPractice.Ch1Lab 7 | { 8 | /// The movie. 9 | public class Movie 10 | { 11 | public const int Childrens = 2; 12 | 13 | public const int Regular = 0; 14 | 15 | public const int New_Release = 1; 16 | 17 | private Price _price; 18 | public string Title { get; } 19 | 20 | public int PriceCode 21 | { 22 | get 23 | { 24 | return this._price.GetPriceCode(); 25 | } 26 | } 27 | 28 | public Movie(string title, int priceCode) 29 | { 30 | this.Title = title; 31 | this.setPrice(priceCode); 32 | } 33 | 34 | public double GetCharge(int daysRented) 35 | { 36 | return this._price.GetCharge(daysRented); 37 | } 38 | 39 | public int GetFrequentRenterPoints(int dayRented) 40 | { 41 | return this._price.GetFrequentRenterPoints(dayRented); 42 | } 43 | 44 | private void setPrice(int priceCode) 45 | { 46 | switch (priceCode) 47 | { 48 | case Regular: 49 | this._price = new RegularPrice(); 50 | break; 51 | 52 | case New_Release: 53 | this._price = new NewReleasePrice(); 54 | break; 55 | 56 | case Childrens: 57 | this._price = new ChildrensPrice(); 58 | break; 59 | } 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /RefactorPractice/Ch1Lab/NewReleasePrice.cs: -------------------------------------------------------------------------------- 1 | namespace RefatorPractice.Ch1Lab 2 | { 3 | public class NewReleasePrice : Price 4 | { 5 | public override int GetPriceCode() 6 | { 7 | return Movie.New_Release; 8 | } 9 | 10 | public override double GetCharge(int daysRented) 11 | { 12 | return daysRented * 3; 13 | } 14 | 15 | public override int GetFrequentRenterPoints(int dayRented) 16 | { 17 | return dayRented > 1 ? 2 : 1; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /RefactorPractice/Ch1Lab/Price.cs: -------------------------------------------------------------------------------- 1 | namespace RefatorPractice.Ch1Lab 2 | { 3 | public abstract class Price 4 | { 5 | public abstract int GetPriceCode(); 6 | 7 | public abstract double GetCharge(int daysRented); 8 | 9 | public virtual int GetFrequentRenterPoints(int dayRented) 10 | { 11 | return 1; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /RefactorPractice/Ch1Lab/RegularPrice.cs: -------------------------------------------------------------------------------- 1 | namespace RefatorPractice.Ch1Lab 2 | { 3 | public class RegularPrice : Price 4 | { 5 | public override int GetPriceCode() 6 | { 7 | return Movie.Regular; 8 | } 9 | 10 | public override double GetCharge(int daysRented) 11 | { 12 | var result = 2.0; 13 | if (daysRented > 2) 14 | { 15 | result += (daysRented - 2) * 1.5; 16 | } 17 | return result; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /RefactorPractice/Ch1Lab/Rental.cs: -------------------------------------------------------------------------------- 1 | namespace RefatorPractice.Ch1Lab 2 | { 3 | public class Rental 4 | { 5 | public Movie Movie { get; } 6 | 7 | public int DayRented { get; } 8 | 9 | public Rental(Movie movie, int dayRented) 10 | { 11 | this.Movie = movie; 12 | this.DayRented = dayRented; 13 | } 14 | 15 | public double GetCharge() 16 | { 17 | return this.Movie.GetCharge(this.DayRented); 18 | } 19 | 20 | public int GetFrequentRenterPoints() 21 | { 22 | return this.Movie.GetFrequentRenterPoints(this.DayRented); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /RefactorPractice/ComposingMethods/ExtractMethod/ExtractMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace RefactorPractice.ComposingMethods.ExtractMethod 7 | { 8 | internal class ExtractMethod 9 | { 10 | private void PrintBanner() 11 | { 12 | Console.WriteLine("**************************"); 13 | Console.WriteLine("***** Customer Owes *****"); 14 | Console.WriteLine("**************************"); 15 | } 16 | 17 | private void PrintOwing() 18 | { 19 | // print banner 20 | this.PrintBanner(); 21 | double outstanding = this.GetOutStanding(); 22 | 23 | // print detail 24 | this.PrintDetail(outstanding); 25 | } 26 | 27 | private double GetOutStanding() 28 | { 29 | var list = new List(); 30 | double result = 0.0; 31 | foreach (var argument in list) 32 | { 33 | result += argument; 34 | } 35 | 36 | return result; 37 | } 38 | 39 | private void PrintDetail(double outstanding) 40 | { 41 | Console.WriteLine("name:Customer"); 42 | Console.WriteLine("amount:" + outstanding); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /RefactorPractice/ComposingMethods/ExtractMethod/README.md: -------------------------------------------------------------------------------- 1 | ## 提煉函式(Extract Method) 2 | 3 | 將這段程式碼放進一個獨立的函式中,並讓函式名稱解釋該函式的用途 4 | 5 | ## 優點: 6 | * 每個函式的粒度小,重用的機會就更大。 7 | * 這也會使高階函式讀起來像是一系列的註釋 8 | * 容易複寫(override) 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /RefactorPractice/ComposingMethods/InlineMethod/InlineMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace RefactorPractice.ComposingMethods.InlineMethod 7 | { 8 | public class InlineMethod 9 | { 10 | private int _moreThanFiveLateDeliveries = 0; 11 | 12 | public int GetRating() 13 | { 14 | return (this._moreThanFiveLateDeliveries > 5) ? 2 : 1; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /RefactorPractice/ComposingMethods/InlineMethod/README.md: -------------------------------------------------------------------------------- 1 | ## 行內函式(Inline Method) 2 | 3 | 一個函式,其本體應該與名稱同樣清楚易懂 4 | 5 | ## 優點: 6 | * 當內部程式碼與函式名稱已經能清楚表達時,就不需要間接層,直接使用函式的內部程式碼即可 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /RefactorPractice/ComposingMethods/InlineTemp/InlineTemp.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace RefactorPractice.ComposingMethods.InlineTemp 7 | { 8 | public class InlineTemp 9 | { 10 | public bool Sample() 11 | { 12 | return (this.GetBasePrice() > 1000); 13 | } 14 | 15 | public double GetBasePrice() 16 | { 17 | return double.MaxValue; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /RefactorPractice/ComposingMethods/InlineTemp/README.md: -------------------------------------------------------------------------------- 1 | ## 行內暫時變數(Inline Temp) 2 | 3 | 一個暫時變數,只被一個簡單運算式賦值過一次,而它妨礙其他重構手法時,將所有對該變數的引用動作,替換成對它賦值的運算式本身。 4 | 5 | ## 優點: 6 | * 當這個暫時變數影響到其他重構時,就應該Inline它 7 | -------------------------------------------------------------------------------- /RefactorPractice/ComposingMethods/IntroduceExplainingVariable/IntroduceExplainingVariable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace RefactorPractice.ComposingMethods.IntroduceExplainingVariable 7 | { 8 | public class IntroduceExplainingVariable 9 | { 10 | private double _quantity = 0; 11 | 12 | private double _itemPrice = 0; 13 | 14 | public double GetPrice() 15 | { 16 | var basePrice = this._quantity * this._itemPrice; 17 | var quantityDiscount = Math.Max(0, this._quantity - 500) * this._itemPrice * 0.05; 18 | var shipping = Math.Min(basePrice * 0.1, 100); 19 | return basePrice - quantityDiscount + shipping; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /RefactorPractice/ComposingMethods/IntroduceExplainingVariable/README.md: -------------------------------------------------------------------------------- 1 | ## 以變數解釋運算式用途(Introduce Explaining Variable) 2 | 3 | 將複雜的運算式(或其中一部分)的結果放進一個暫時變數,用變數名稱來解釋運算式的用途。 4 | 5 | ## 優點: 6 | * 將運算式分解成容易理解與管理的形式 7 | * 在較長的演算法中,可以用暫時變數來解釋每一步運算的意義 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/ComposingMethods/RemoveAssignmentsToParameters/README.md: -------------------------------------------------------------------------------- 1 | ## 移除對參數賦值(Remove Assignments To Parameters) 2 | 3 | 程式碼對一個參數進行賦值,用一個暫時變數取代該參數的位置 4 | 5 | ## 優點: 6 | * 如果是pass by value,不會造成呼叫端的影響 7 | * 如果是pass by reference,會造成程式碼閱讀者混淆 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /RefactorPractice/ComposingMethods/RemoveAssignmentsToParameters/RemoveAssignentToParameters.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace RefactorPractice.ComposingMethods.RemoveAssignmentsToParameters 7 | { 8 | public class RemovAssignentToParameters 9 | { 10 | public int Discount(int inputVal, int quantity, int yearToDate) 11 | { 12 | int result = inputVal; 13 | if (inputVal > 50) 14 | { 15 | result -= 2; 16 | } 17 | 18 | if (quantity > 100) 19 | { 20 | result -= 1; 21 | } 22 | 23 | if (yearToDate > 10000) 24 | { 25 | result -= 4; 26 | } 27 | return result; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /RefactorPractice/ComposingMethods/ReplaceMethodWithMethodObject/README.md: -------------------------------------------------------------------------------- 1 | ## 用函式物件替換函式(Replace Method With Method Object) 2 | 3 | 有一個大型函式,其中對區域變數的使用,使你無法採用提煉函式(Extract Method) 4 | 將這個函式獨立放進一個單獨物件中,這樣區域變數就會變成物件內的欄位(field), 5 | 然後就可以在同一個物件中將這個大型函式分解為數個小函式。 6 | 7 | ## 優點: 8 | * 可以創造出新函式,提高重用性,並可以將原本大型函式拆解變短 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /RefactorPractice/ComposingMethods/ReplaceMethodWithMethodObject/ReplaceMethodWithMethodObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace RefactorPractice.ComposingMethods.ReplaceMethodWithMethodObject 7 | { 8 | public class ReplaceMethodWithMethodObject 9 | { 10 | public class Account 11 | { 12 | public int Gamma(int inputVal, int quantity, int yearToDate) 13 | { 14 | return new Gamma(this, inputVal, quantity, yearToDate).Compute(); 15 | } 16 | 17 | public int Delta() 18 | { 19 | return 0; 20 | } 21 | } 22 | 23 | public class Gamma 24 | { 25 | private Account _account; 26 | 27 | private int inputVal; 28 | 29 | private int quantity; 30 | 31 | private int yearToDate; 32 | 33 | private int importantValue1; 34 | 35 | private int importantValue2; 36 | 37 | private int importantValue3; 38 | 39 | public Gamma(Account source, int inputValArg, int quantityArg, int yearToDateArg) 40 | { 41 | this._account = source; 42 | this.inputVal = inputValArg; 43 | this.quantity = quantityArg; 44 | this.yearToDate = yearToDateArg; 45 | } 46 | 47 | public int Compute() 48 | { 49 | int importantValue1 = (this.inputVal * this.quantity) + this._account.Delta(); 50 | int importantValue2 = (this.inputVal * this.yearToDate) + 100; 51 | this.ImportantThing(); 52 | int importantValue3 = importantValue2 * 7; 53 | return importantValue3 - 2 * importantValue1; 54 | } 55 | 56 | private void ImportantThing() 57 | { 58 | if ((this.yearToDate - this.importantValue1) > 100) 59 | { 60 | this.importantValue2 -= 20; 61 | } 62 | } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /RefactorPractice/ComposingMethods/ReplaceTempWithQuery/README.md: -------------------------------------------------------------------------------- 1 | ## 用回傳函式替換暫時變數(Replace Temp With Query) 2 | 3 | 當一個暫時變數保存某一運算式的結果,將這個運算式提煉到獨立函式,將這個暫時變數的所有被引用點替換成對新函式的呼叫。新函式可以被其他函示使用。 4 | 5 | ## 優點: 6 | * 同一個Class都可以使用新的函式 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /RefactorPractice/ComposingMethods/ReplaceTempWithQuery/ReplaceTempWithQuery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace RefactorPractice.ComposingMethods.ReplaceTempWithQuery 7 | { 8 | public class ReplaceTempWithQuery 9 | { 10 | private double _quantity; 11 | 12 | private double _itemPrice; 13 | 14 | public double Sample() 15 | { 16 | if (this.GetBasePrice() > 1000) 17 | { 18 | return this.GetBasePrice() * 0.95; 19 | } 20 | else 21 | { 22 | return this.GetBasePrice() * 0.98; 23 | } 24 | } 25 | 26 | private double GetBasePrice() 27 | { 28 | return this._quantity * this._itemPrice; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /RefactorPractice/ComposingMethods/SplitTemporaryVariable/README.md: -------------------------------------------------------------------------------- 1 | ## 剖解暫時變數(Split Temporary Variable) 2 | 3 | 當某個暫時變數賦值超過一次,它既不是迴圈變數,也不是累加暫時變數,針對每一次賦值,創造一個獨立、對應的暫時變數 4 | 5 | ## 優點: 6 | * 如果一個暫時變數賦值超過一次,代表承擔過多責任,就應該被替換成多個暫時變數,這樣會使程式碼閱讀者更加容易理解。 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /RefactorPractice/ComposingMethods/SplitTemporaryVariable/SplitTemporaryVariable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace RefactorPractice.ComposingMethods.SplitTemporaryVariable 7 | { 8 | public class SplitTemporaryVarible 9 | { 10 | private double _primaryForce; 11 | 12 | private double _mass; 13 | 14 | private int _delay; 15 | 16 | private double _secondaryForce; 17 | 18 | public double GetDistanceTravelled(int time) 19 | { 20 | double result; 21 | double primaryAcc = this._primaryForce / this._mass; 22 | int primaryTime = Math.Min(time, this._delay); 23 | result = 0.5 * primaryAcc * primaryTime * primaryTime; 24 | int secondaryTime = time - this._delay; 25 | if (secondaryTime > 0) 26 | { 27 | double primaryVel = primaryAcc * this._delay; 28 | double secondaryAcc = (this._primaryForce + this._secondaryForce) / this._mass; 29 | result += primaryVel * secondaryTime + 0.5 * secondaryAcc * secondaryTime * secondaryTime; 30 | } 31 | return result; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /RefactorPractice/ComposingMethods/SubstituteAlgorithm/README.md: -------------------------------------------------------------------------------- 1 | ## 替換演算法(Substitute Algorithm) 2 | 3 | 將函式本體替換成另一個演算法 4 | 5 | ## 優點: 6 | * 用簡單的演算法取代原本不好的寫法 7 | * 在使用前,請先確認盡可能已經了解原有函式, 8 | 替換一個巨大複雜的演算法是困難的,只有將它分解為簡單的小函式, 9 | 才能有把握地進行演算法替換工作。 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /RefactorPractice/ComposingMethods/SubstituteAlgorithm/SubstituteAlgorithm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace RefactorPractice.ComposingMethods.SubstituteAlgorithm 7 | { 8 | public class SubstituteAlgorithm 9 | { 10 | public string FoundPerson(string[] people) 11 | { 12 | var candidates = new List { "Don", "John", "Kent" }; 13 | 14 | for (int i = 0; i < people.Length; i++) 15 | { 16 | if (candidates.Contains(people[i])) 17 | { 18 | return people[i]; 19 | } 20 | } 21 | 22 | return string.Empty; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /RefactorPractice/DealWithGeneralization/CollapseHierarchy/README.md: -------------------------------------------------------------------------------- 1 | ## 摺疊繼承關係(Collapse Hierarchy) 2 | 3 | superclass與subclass無太大差別 4 | 5 | 將他們合為一體 6 | 7 | ## 優點: 8 | * 重構繼承體系,往往是將函式和欄位在體系中上下移動。完成動作後,把不需要的class合併起來 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/DealWithGeneralization/ExtractInterface/ExtractInterface.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.DealWithGeneralization.ExtractInterface 8 | { 9 | public class ExtractInterface 10 | { 11 | public class Employee : IBillable 12 | { 13 | private int _rate; 14 | 15 | public int Rate 16 | { 17 | get 18 | { 19 | return this._rate; 20 | } 21 | } 22 | 23 | public bool HasSpecialSkill() 24 | { 25 | return true; 26 | } 27 | } 28 | 29 | public double Charge(IBillable employee, int days) 30 | { 31 | int result = employee.Rate * days; 32 | if (employee.HasSpecialSkill()) 33 | { 34 | return result * 1.05; 35 | } 36 | return result; 37 | } 38 | 39 | public interface IBillable 40 | { 41 | int Rate { get; } 42 | 43 | bool HasSpecialSkill(); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /RefactorPractice/DealWithGeneralization/ExtractInterface/README.md: -------------------------------------------------------------------------------- 1 | ## 提煉類別(Extract Interface) 2 | 3 | 客戶使用class介面中的同一子集,或者兩個class的介面有部分相同 4 | 5 | 將相同子集提煉到一個獨立介面中 6 | 7 | ## 優點: 8 | * 將被使用的部分責任分離出來,使系統的用法更清晰,系統責任劃分也更清楚。 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/DealWithGeneralization/ExtractSubClass/ExtractSubClass.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net.Http.Headers; 5 | using System.Security.AccessControl; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace RefactorPractice.DealWithGeneralization.ExtractSubClass 10 | { 11 | public class ExtractSubClass 12 | { 13 | public void Sample() 14 | { 15 | JobItem jobitem = new JobItem(0, 5); 16 | } 17 | 18 | public class JobItem 19 | { 20 | private int _unitPrice; 21 | 22 | private int _quantity; 23 | 24 | public int UnitPrice 25 | { 26 | get 27 | { 28 | return this._unitPrice; 29 | } 30 | } 31 | 32 | public int Quantity 33 | { 34 | get 35 | { 36 | return this._quantity; 37 | } 38 | } 39 | 40 | public bool IsLabor 41 | { 42 | get 43 | { 44 | return false; 45 | } 46 | } 47 | 48 | public int TotalPrice 49 | { 50 | get 51 | { 52 | return this.UnitPrice * this._quantity; 53 | } 54 | } 55 | 56 | public JobItem(int unitPrice, int quantity) 57 | { 58 | this._unitPrice = unitPrice; 59 | this._quantity = quantity; 60 | } 61 | } 62 | 63 | public class LaborItem : JobItem 64 | { 65 | private int _unitPrice; 66 | 67 | protected Employee _employee; 68 | 69 | public Employee Employee 70 | { 71 | get 72 | { 73 | return this._employee; 74 | } 75 | } 76 | 77 | public bool IsLabor 78 | { 79 | get 80 | { 81 | return true; 82 | } 83 | } 84 | 85 | public int UnitPrice 86 | { 87 | get 88 | { 89 | return this._employee.Rate; 90 | } 91 | } 92 | 93 | public LaborItem(int unitPrice, int quantity, Employee employee) 94 | : base(0, quantity) 95 | { 96 | this._employee = employee; 97 | } 98 | } 99 | 100 | public class Employee 101 | { 102 | private int _rate; 103 | 104 | public int Rate 105 | { 106 | get 107 | { 108 | return this._rate; 109 | } 110 | } 111 | 112 | public Employee(int rate) 113 | { 114 | this._rate = rate; 115 | } 116 | } 117 | } 118 | } -------------------------------------------------------------------------------- /RefactorPractice/DealWithGeneralization/ExtractSubClass/README.md: -------------------------------------------------------------------------------- 1 | ## 提煉子類別(Extract Subclass) 2 | 3 | class中的某些特性只被某些實體用到 4 | 5 | 新建一個subclass,將上面那些特性移到subclass中 6 | 7 | ## 優點: 8 | * 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/DealWithGeneralization/ExtractSuperClass/ExtractSuperClass.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace RefactorPractice.DealWithGeneralization.ExtractSuperClass 9 | { 10 | public class ExtractSuperClass 11 | { 12 | public abstract class Party 13 | { 14 | protected Party(string name) 15 | { 16 | this._name = name; 17 | } 18 | 19 | protected string _name; 20 | 21 | public string Name 22 | { 23 | get 24 | { 25 | return this._name; 26 | } 27 | } 28 | 29 | public abstract int GetAnnualCost(); 30 | } 31 | 32 | public class Employee : Party 33 | { 34 | private string _id; 35 | 36 | private int _annualCost; 37 | 38 | public string Id 39 | { 40 | get 41 | { 42 | return this._id; 43 | } 44 | } 45 | 46 | public int AnnualCost 47 | { 48 | get 49 | { 50 | return this._annualCost; 51 | } 52 | } 53 | 54 | public Employee(string name, string id, int annualCost) : base(name) 55 | { 56 | this._id = id; 57 | this._annualCost = annualCost; 58 | } 59 | 60 | public override int GetAnnualCost() 61 | { 62 | return this.AnnualCost; 63 | } 64 | } 65 | 66 | public class Department : Party 67 | { 68 | public List _employees = new List(); 69 | 70 | public Department(string name) : base(name) 71 | { 72 | } 73 | 74 | public override int GetAnnualCost() 75 | { 76 | return this._employees.Sum(item => item.AnnualCost); 77 | } 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /RefactorPractice/DealWithGeneralization/ExtractSuperClass/README.md: -------------------------------------------------------------------------------- 1 | ## 提煉超類別(Extract Superclass) 2 | 3 | 兩個classes有相似特性 4 | 5 | 為這兩個class建立一個superclass,將相同特性移至superclass 6 | 7 | ## 優點: 8 | * 當發現具有共通性的這些class之後,在著手建立之間的繼承結構 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/DealWithGeneralization/FormTemplateMethod/FormTemplateMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.DealWithGeneralization.FormTemplateMethod 8 | { 9 | internal class FormTemplateMethod 10 | { 11 | public class Customer 12 | { 13 | private List _rentals; 14 | 15 | public List Rental 16 | { 17 | get 18 | { 19 | return this._rentals; 20 | } 21 | } 22 | 23 | public string Name { get; set; } 24 | 25 | public string GetTotalFrequentRenterPoints() 26 | { 27 | throw new NotImplementedException(); 28 | } 29 | 30 | public string GetTotalCharge() 31 | { 32 | throw new NotImplementedException(); 33 | } 34 | } 35 | 36 | public class Rental 37 | { 38 | public Movie Movie { get; set; } 39 | 40 | public decimal Charge { get; set; } 41 | } 42 | 43 | public class Movie 44 | { 45 | public string Title { get; set; } 46 | } 47 | 48 | public abstract class Statement 49 | { 50 | public string Value(Customer customer) 51 | { 52 | string result = this.HeaderString(customer); 53 | foreach (var rental in customer.Rental) 54 | { 55 | result += this.EachRentalString(rental); 56 | } 57 | result = this.FooterString(customer); 58 | return result; 59 | } 60 | 61 | public abstract string HeaderString(Customer customer); 62 | 63 | public abstract string EachRentalString(Rental rental); 64 | 65 | public abstract string FooterString(Customer customer); 66 | } 67 | 68 | public class TextStatement : Statement 69 | { 70 | public override string FooterString(Customer customer) 71 | { 72 | var result = "Amount owed is" + customer.GetTotalCharge() + "\n"; 73 | result += "You earned " + customer.GetTotalFrequentRenterPoints() + "frequent renter points"; 74 | return result; 75 | } 76 | 77 | public override string EachRentalString(Rental rental) 78 | { 79 | return rental.Movie.Title + "\t" + rental.Charge + "\n"; 80 | } 81 | 82 | public override string HeaderString(Customer customer) 83 | { 84 | return "Rental Record for" + customer.Name + "\n"; 85 | } 86 | } 87 | 88 | public class HtmlStatement : Statement 89 | { 90 | public override string FooterString(Customer customer) 91 | { 92 | var result = "

You owe " + customer.GetTotalCharge() + "

\n"; 93 | result += "On this rental you earned " + customer.GetTotalFrequentRenterPoints() 94 | + " frequent renter points

"; 95 | return result; 96 | } 97 | 98 | public override string EachRentalString(Rental rental) 99 | { 100 | return rental.Movie.Title + ":" + rental.Charge + "
\n"; 101 | } 102 | 103 | public override string HeaderString(Customer customer) 104 | { 105 | return "

Rentals for " + customer.Name + "

\n"; 106 | } 107 | } 108 | } 109 | } -------------------------------------------------------------------------------- /RefactorPractice/DealWithGeneralization/FormTemplateMethod/README.md: -------------------------------------------------------------------------------- 1 | ## 塑造模板函式(Form Template Method) 2 | 3 | 有一些subclass,其中相應的某些函式以相同順序執行類似的功能,但各功能有些不同 4 | 5 | 將各功能分別放進獨立函式中,並保持他們相同的署名,於是原函式也變得相同。然後將原函式上移至superclass 6 | 7 | ## 優點: 8 | * 繼承是避免重複的強大工具 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/DealWithGeneralization/PullUpConstructorBody/PullUpConstructorBody.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.DealWithGeneralization.PullUpConstructorBody 8 | { 9 | public class PullUpConstructorBody 10 | { 11 | public class Employee 12 | { 13 | protected string _name; 14 | 15 | protected string _id; 16 | 17 | protected Employee(string name, string id) 18 | { 19 | this._name = name; 20 | this._id = id; 21 | } 22 | } 23 | 24 | public class Manager : Employee 25 | { 26 | private int _grade; 27 | 28 | public Manager(string name, string id, int grade) : base(name, id) 29 | { 30 | this._grade = grade; 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /RefactorPractice/DealWithGeneralization/PullUpConstructorBody/README.md: -------------------------------------------------------------------------------- 1 | ## 建構式本體上移(Pull Up Constrctor Body) 2 | 3 | 在各個subclass中擁有一些建構式,他們的本體幾乎一致 4 | 5 | 在superclass中新建一個建構式,並在subclass建構式中呼叫他 6 | 7 | ## 優點: 8 | * 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/DealWithGeneralization/PullUpField/README.md: -------------------------------------------------------------------------------- 1 | ## 欄位上移(Pull Up Field) 2 | 3 | 兩個subclasses擁有相同的欄位 4 | 5 | 將此一欄位移至superclass 6 | 7 | ## 優點: 8 | * 如果subclass都有重複的特性,移到superclass使用 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/DealWithGeneralization/PullUpMethod/README.md: -------------------------------------------------------------------------------- 1 | ## 函式上移(Pull Up Method) 2 | 3 | 有些函式,在各個subclass中產生完全相同的結果 4 | 5 | 將該函式移至superclass 6 | 7 | ## 優點: 8 | * 只要系統發現有兩個重複的函式,就提升函式到superclass,避免重複。 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/DealWithGeneralization/PushDownField/README.md: -------------------------------------------------------------------------------- 1 | ## 欄位下移(Push Down Field) 2 | 3 | superclass中的某個欄位只被部分(而非全部)subclass使用 4 | 5 | 將這個欄位移到需要他的那些subclass去 6 | 7 | ## 優點: 8 | * 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/DealWithGeneralization/PushDownMethod/README.md: -------------------------------------------------------------------------------- 1 | ## 函式下移(Push Down Method) 2 | 3 | superclass中的某個函式只與部分(而非全部)subclasses有關 4 | 5 | 將這個函式移到相關的那些subclass去 6 | 7 | ## 優點: 8 | * 當有必要把某些行為移到subclass就使用此手法。 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/DealWithGeneralization/ReplaceDelegationWithInheritance/README.md: -------------------------------------------------------------------------------- 1 | ## 以繼承取代委託(Replace Delegation With Inheritance) 2 | 3 | 兩個class之間使用委託關係,並經常為整個介面編寫極簡單的請託函式 4 | 5 | 讓請託class繼承受託class 6 | 7 | ## 優點: 8 | * 如果沒有全部使用受託class的所有函式,就不該使用此守則 9 | * 如果受託物件不只被一個物件共享,而受託物件是可變的,就不可使用此守則 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /RefactorPractice/DealWithGeneralization/ReplaceDelegationWithInheritance/ReplaceDelegationWithInheritance.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.DealWithGeneralization.ReplaceDelegationWithInheritance 8 | { 9 | internal class ReplaceDelegationWithInheritance 10 | { 11 | public class Employee : Person 12 | { 13 | public string GetName() 14 | { 15 | return base.Name; 16 | } 17 | 18 | public void SetName(string name) 19 | { 20 | base.Name = name; 21 | } 22 | 23 | public string ToString() 24 | { 25 | return "Emp:" + base.LastName; 26 | } 27 | } 28 | 29 | public class Person 30 | { 31 | public string Name { get; set; } 32 | 33 | public string LastName 34 | { 35 | get 36 | { 37 | return this.Name.Substring(this.Name.LastIndexOf("") + 1); 38 | } 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /RefactorPractice/DealWithGeneralization/ReplaceInheritanceWithDelegation/README.md: -------------------------------------------------------------------------------- 1 | ## 以委託取代繼承 (Replace Inheritance With Delegation) 2 | 3 | 某個subclass只使用superclass介面的一部分,或是根本不需要繼承來的資料 4 | 5 | 在subclass中新建一個欄位保存superclass,調整subclass使他改而委託superclass,然後去掉兩者之間的繼承關係 6 | 7 | ## 優點: 8 | * 以委託取代繼承,可以清楚的表明,只需要受託類別的哪一些部分 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/DealWithGeneralization/ReplaceInheritanceWithDelegation/ReplaceInheritanceWithDelegation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace RefactorPractice.DealWithGeneralization.ReplaceIngeritanceWithDelegation 9 | { 10 | internal class ReplaceInheritanceWithDelegation 11 | { 12 | public class MyStack 13 | { 14 | private ArrayList _arrayList; 15 | 16 | public void Push(Object obj) 17 | { 18 | this._arrayList.Insert(0, obj); 19 | } 20 | 21 | public Object Pop() 22 | { 23 | var result = this._arrayList[0]; 24 | this._arrayList.RemoveAt(0); 25 | return result; 26 | } 27 | 28 | public int Size() 29 | { 30 | return this._arrayList.Count; 31 | } 32 | 33 | public bool IsEmpty() 34 | { 35 | return this._arrayList.Count == 0; 36 | } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/AddParameter/README.md: -------------------------------------------------------------------------------- 1 | ## 添加參數(Add Parameter) 2 | 3 | 某個函式需要從呼叫端得到更多資訊 4 | 5 | 為此函式添加一個物件參數,讓該物件帶進函式所需資訊 6 | 7 | ## 優點: 8 | * 使用時要注意,過長的參數列不是一個好現象,可以適時使用Parameter Object來減少過長的參數列 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/EncapsulateDowncast/EncapsulateDowncast.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.MakingMethodCallsSimpler.EncapsulateDowncast 8 | { 9 | public class EncapsulateDowncast 10 | { 11 | public class Reading 12 | { 13 | private List _readings = new List(); 14 | 15 | public Reading LastReading() 16 | { 17 | return this._readings.LastOrDefault(); 18 | } 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/EncapsulateDowncast/README.md: -------------------------------------------------------------------------------- 1 | ## 封裝「向下轉型」動作 (Encapsulate Downcast) 2 | 3 | 某個函式傳回的物件,需要由函式呼叫者執行「向下轉型」(downcast)動作 4 | 5 | 將向下轉型(downcast)動作移到函式中 6 | 7 | ## 優點: 8 | * 盡量避免由使用者來承擔向下轉型的責任,要提供準確的型別給使用者 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/HideMethod/README.md: -------------------------------------------------------------------------------- 1 | ## 隱藏某個函式(Hide Method) 2 | 3 | 有一個函式,從來沒有被其他任何class用到 4 | 5 | 將這個函式修改為private 6 | 7 | 8 | ## 優點: 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/IntroduceParameterObject/IntroduceParameterObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.MakingMethodCallsSimpler.IntroduceParameterObject 8 | { 9 | public class IntroduceParameterObject 10 | { 11 | public void Sample() 12 | { 13 | var account = new Account(); 14 | var flow = account.GetFlowBetween(new DateRange(new DateTime(2017, 10, 12), new DateTime(2017, 10, 12))); 15 | } 16 | 17 | public class Entry 18 | { 19 | private double _value; 20 | 21 | private DateTime _chargeDate; 22 | 23 | public double Value 24 | { 25 | get 26 | { 27 | return this._value; 28 | } 29 | } 30 | 31 | public DateTime ChargeDateTime 32 | { 33 | get 34 | { 35 | return this._chargeDate; 36 | } 37 | } 38 | 39 | public Entry(double value, DateTime chargeDate) 40 | { 41 | this._value = value; 42 | this._chargeDate = chargeDate; 43 | } 44 | } 45 | 46 | public class Account 47 | { 48 | private List _entries = new List(); 49 | 50 | public double GetFlowBetween(DateRange range) 51 | { 52 | double result = 0; 53 | foreach (var entry in _entries) 54 | { 55 | if (range.InCludes(entry.ChargeDateTime)) 56 | { 57 | result += entry.Value; 58 | } 59 | } 60 | 61 | return result; 62 | } 63 | } 64 | 65 | public class DateRange 66 | { 67 | private DateTime _start; 68 | 69 | private DateTime _end; 70 | 71 | public DateTime Start 72 | { 73 | get 74 | { 75 | return this._start; 76 | } 77 | } 78 | 79 | public DateTime End 80 | { 81 | get 82 | { 83 | return this._end; 84 | } 85 | } 86 | 87 | public DateRange(DateTime start, DateTime end) 88 | { 89 | this._start = start; 90 | this._end = end; 91 | } 92 | 93 | public bool InCludes(DateTime arg) 94 | { 95 | return arg >= this._start || arg <= this._end; 96 | } 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/IntroduceParameterObject/README.md: -------------------------------------------------------------------------------- 1 | ## 引入參數物件(Introduce Parameter Object) 2 | 3 | 某些參數總是很自然的同時出現 4 | 5 | 以一個物件取代這些參數 6 | 7 | ## 優點: 8 | * 用參數物件可以縮短參數長度 9 | * 新物件的存取函式使程式碼更一致 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/ParameterizeMethod/ParameterizeMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.Remoting.Channels; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace RefactorPractice.MakingMethodCallsSimpler.ParameterizeMethod 10 | { 11 | internal class ParameterizeMethod 12 | { 13 | private class Employee 14 | { 15 | protected double BaseCharge() 16 | { 17 | double result = UsageInRange(0, 100) * 0.03; 18 | result += UsageInRange(100, 200) * 0.05; 19 | result += UsageInRange(200, Int32.MaxValue) * 0.07; 20 | return result; 21 | } 22 | 23 | private double UsageInRange(int start, int end) 24 | { 25 | if (this.LastUsage() > start) 26 | { 27 | return Math.Min(this.LastUsage(), end) - start; 28 | } 29 | return 0; 30 | } 31 | 32 | private double LastUsage() 33 | { 34 | throw new NotImplementedException(); 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/ParameterizeMethod/README.md: -------------------------------------------------------------------------------- 1 | ## 令函式攜帶參數(Parameterize Method) 2 | 3 | 有幾個函式做了類似的工作,但在函式本體中包含了不同的值。 4 | 5 | 建立單一函式,以表達那些不同的值 6 | 7 | ## 優點: 8 | * 這樣可以去除重複的程式碼,提高靈活性。 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/PreserveWholeObject/PreserveWholeObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.MakingMethodCallsSimpler.PreserveWholeObject 8 | { 9 | internal class PreserveWholeObject 10 | { 11 | public class Room 12 | { 13 | public bool WithinPlan(HeatingPlan plan) 14 | { 15 | return plan.WithinRange(this.DaysTempRange()); 16 | } 17 | 18 | private TempRange DaysTempRange() 19 | { 20 | return new TempRange(); 21 | } 22 | } 23 | 24 | public class HeatingPlan 25 | { 26 | public bool WithinRange(TempRange roomRange) 27 | { 28 | return (roomRange.Low > roomRange.Low && roomRange.High <= roomRange.High); 29 | } 30 | } 31 | 32 | public class TempRange 33 | { 34 | public int Low { get; set; } 35 | 36 | public int High { get; set; } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/PreserveWholeObject/README.md: -------------------------------------------------------------------------------- 1 | ## 保持物件完整(Preserve Whole Object) 2 | 3 | 從某個物件中取得值,將他們作為某一次函式呼叫的參數。 4 | 5 | 改為傳遞整個物件 6 | 7 | ## 優點: 8 | * 可以使參數列更穩固,還能提高可讀性。 9 | * 但傳遞整個物件,參數物件與被呼叫函式物件就有依存關係,如果會使依存結構惡化,就不要用這個原則。 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/RemoveParameter/README.md: -------------------------------------------------------------------------------- 1 | ## 移除參數(Remove Parameter) 2 | 3 | 函式本體不再需要某個參數 4 | 5 | 將該參數去除 6 | 7 | ## 優點: 8 | * 當不需要參數的時候一定要移除,不然會造成程式碼閱讀者混淆。 9 | * 對於多型函式,先要了解參數是不是被其他多型函式所使用,這時候就不能移除,或是建立另一個函式來使用。 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/RemoveSettingMethod/README.md: -------------------------------------------------------------------------------- 1 | ## 移除設值函式(Remove Setting Method) 2 | 3 | 某個Class中的某個欄位,應該在物件初創時就被設值,然後就不再改變 4 | 5 | 去掉該欄位的所有設值函式(setter) 6 | 7 | ## 優點: 8 | * 如果不希望欄位值在初創之後還會被變更,就不要提供設值函式 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/RemoveSettingMethod/RemoveSettingMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.MakingMethodCallsSimpler.RemoveSettingMethod 8 | { 9 | public class RemoveSettingMethod 10 | { 11 | public class Account 12 | { 13 | private string _id; 14 | 15 | public Account(string id) 16 | { 17 | this._id = id; 18 | } 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/RenameMethod/README.md: -------------------------------------------------------------------------------- 1 | ## 重新命名函式(Rename Method) 2 | 3 | 函式的名稱未能揭示函式的用途。 4 | 5 | 修改函式名稱 6 | 7 | ## 優點: 8 | * 函式的名稱應該準確表達他的用途,程式是人寫的,所以需要良好的命名,提高程式的可讀性。 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/RenameMethod/RenameMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.MakingMethodCallsSimpler.RenameMethod 8 | { 9 | public class RenameMethod 10 | { 11 | private string _officeNumber; 12 | 13 | private string _officeAreaCode; 14 | 15 | public string GetTelephoneNumber() 16 | { 17 | return GetOfficeTelephoneNumber(); 18 | } 19 | 20 | private string GetOfficeTelephoneNumber() 21 | { 22 | return $"({_officeAreaCode}){_officeNumber}"; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/ReplaceConstructorWithFactoryMethod/README.md: -------------------------------------------------------------------------------- 1 | ## 以「工廠函式」取代「建構式」(Replace Constructor With Factory Method) 2 | 3 | 創建物件時不僅僅是對它做簡單的建構動作 4 | 5 | 將constructor(建構式)替換為factory method(工廠函式) 6 | 7 | ## 優點: 8 | * 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/ReplaceConstructorWithFactoryMethod/ReplaceConstructorWithFactoryMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace RefactorPractice.MakingMethodCallsSimpler.ReplaceConstructorWithFactoryMethod 9 | { 10 | public class ReplaceConstructorWithFactoryMethodByTypeCode 11 | { 12 | public void Sample() 13 | { 14 | var employee = Employee.Create(Employee.ENGINEER); 15 | } 16 | 17 | public class Employee 18 | { 19 | private int _type; 20 | 21 | public const int ENGINEER = 0; 22 | public const int SALESMAN = 1; 23 | public const int MANAGER = 2; 24 | 25 | private Employee(int type) 26 | { 27 | this._type = type; 28 | } 29 | 30 | public static Employee Create(int type) 31 | { 32 | return new Employee(type); 33 | } 34 | } 35 | } 36 | 37 | public class ReplaceConstructorWithFactoryMethodByString 38 | { 39 | public class Employee 40 | { 41 | private int _type; 42 | 43 | public const int ENGINEER = 0; 44 | public const int SALESMAN = 1; 45 | public const int MANAGER = 2; 46 | 47 | public static Employee Create(string name) 48 | { 49 | var result = Assembly.GetExecutingAssembly(); 50 | return result.CreateInstance(name) as Employee; 51 | } 52 | } 53 | 54 | public class SalesMan : Employee 55 | { 56 | } 57 | 58 | public class Manager : Employee 59 | { 60 | } 61 | 62 | public class Engineer : Employee 63 | { 64 | } 65 | } 66 | 67 | public class ReplaceConstructorWithFactoryMethodByExplicitMethod 68 | { 69 | public void Sample() 70 | { 71 | Person person = Person.CreateFemale(); 72 | Person person2 = Person.CreateMale(); 73 | } 74 | 75 | public class Person 76 | { 77 | public static Person CreateMale() 78 | { 79 | return new Male(); 80 | } 81 | 82 | public static Person CreateFemale() 83 | { 84 | return new Female(); 85 | } 86 | } 87 | 88 | public class Male : Person 89 | { 90 | } 91 | 92 | public class Female : Person 93 | { 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/ReplaceErrorCodeWithException/README.md: -------------------------------------------------------------------------------- 1 | ## 以異常取代錯誤碼(Replace Error Code With Exception) 2 | 3 | 某個函式傳回一個特殊代碼(Special Code),用以表示某種錯誤狀況 4 | 5 | 改用異常(Exception) 6 | 7 | ## 優點: 8 | * Exception清楚的將一般程式與錯誤處理分開了,使得程式更加容易理解 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/ReplaceErrorCodeWithException/ReplaceErrorCodeWithException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.MakingMethodCallsSimpler.ReplaceErrorCodeWithException 8 | { 9 | public class ReplaceErrorCodeWithException 10 | { 11 | private int _balance; 12 | 13 | public void Withdraw(int amount) 14 | { 15 | if (amount > _balance) 16 | { 17 | throw new Exception("錯誤"); 18 | } 19 | this._balance -= amount; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/ReplaceExceptionWithTest/README.md: -------------------------------------------------------------------------------- 1 | ## 以測試取代異常(Replace Exception With Test) 2 | 3 | 面對一個呼叫者可預先加以檢查的條件,你拋出一個異常 4 | 5 | 修改呼叫者,使它在呼叫函式之前先做檢查 6 | 7 | ## 優點: 8 | * 提供一個測試,呼叫函式前先進行測試 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/ReplaceParameterWithExplicitMethod/README.md: -------------------------------------------------------------------------------- 1 | ## 以明確函式取代參數(Replace Parameter With Explicit Method) 2 | 3 | 有一個函式,其內完全取決於參數值而採取不同的反應。 4 | 5 | 針對該參數的每一個可能值,建立一個獨立函式 6 | 7 | 8 | ## 優點: 9 | * 如果某個參數有離散取值,而函式內又以條件式檢查這些參數,並根據不同參數值做不同反應,就使用此原則。 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/ReplaceParameterWithExplicitMethod/ReplaceParameterWithExplicitMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | using RefactorPractice.OrganizingData.ReplaceTypeCodeWithStateOrStrategy; 8 | 9 | namespace RefactorPractice.MakingMethodCallsSimpler.ReplaceParameterWithExplicitMethod 10 | { 11 | internal class ReplaceParameterWithExplicitMethod 12 | { 13 | public void Sample() 14 | { 15 | //Employee kent = Employee.Create(Employee.ENGINEER); 16 | Employee kent = Employee.CreateEngineer(); 17 | } 18 | 19 | public class Employee 20 | { 21 | public const int ENGINEER = 0; 22 | 23 | public const int SALESMAN = 1; 24 | 25 | public const int MANAGER = 2; 26 | 27 | public static Employee CreateEngineer() 28 | { 29 | return new Engineer(); 30 | } 31 | 32 | public static Employee CreateSalesMan() 33 | { 34 | return new SalesMan(); 35 | } 36 | 37 | public static Employee CreateManager() 38 | { 39 | return new Manager(); 40 | } 41 | } 42 | 43 | public class Engineer : Employee 44 | { 45 | } 46 | 47 | public class SalesMan : Employee 48 | { 49 | } 50 | 51 | public class Manager : Employee 52 | { 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/ReplaceParameterWithMethods/README.md: -------------------------------------------------------------------------------- 1 | ## 以函式取代參數(Replace Parameter With Methods) 2 | 3 | 物件使用某個函式,並將結果作為參數,傳遞給另一個函式。而接受該參數的函式也可以使用某個函式 4 | 5 | 6 | ## 優點: 7 | * 如果某個參數有離散取值,而函式內又以條件式檢查這些參數,並根據不同參數值做不同反應,就使用此原則。 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/ReplaceParameterWithMethods/ReplaceParameterWithMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.MakingMethodCallsSimpler.ReplaceParameterWithMethods 8 | { 9 | internal class ReplaceParameterWithMethods 10 | { 11 | private int _itemPrice; 12 | 13 | private int _quantity; 14 | 15 | public double GetPrice() 16 | { 17 | return DiscountedPrice(); 18 | } 19 | 20 | private int GetBasePrice() 21 | { 22 | return _quantity * _itemPrice; 23 | } 24 | 25 | private int GetDiscountLevel() 26 | { 27 | int discountLevel; 28 | if (_quantity > 100) 29 | { 30 | discountLevel = 2; 31 | } 32 | else 33 | { 34 | discountLevel = 1; 35 | } 36 | 37 | return discountLevel; 38 | } 39 | 40 | private double DiscountedPrice() 41 | { 42 | if (GetDiscountLevel() == 2) 43 | { 44 | return this.GetBasePrice() * 0.1; 45 | } 46 | else 47 | { 48 | return this.GetBasePrice() * 0.05; 49 | } 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/SeparateQueryFromModifier/README.md: -------------------------------------------------------------------------------- 1 | ## 將查詢函式和修改函式分離(Separate Query From Modifier) 2 | 3 | 某個函式傳回物件狀態值,又修改物件狀態。 4 | 5 | 建立兩個不同函式,其中一個負責查詢,一個負責修改 6 | 7 | ## 優點: 8 | * 如果函式只向你提供一個值,沒有修改物件狀態,那可以很容易的將函式搬離。 9 | 若函式向你回傳物件又修改物件狀態,容易產生副作用 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /RefactorPractice/MakingMethodCallsSimpler/SeparateQueryFromModifier/SeparateQueryFromModifier.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace RefactorPractice.MakingMethodCallsSimpler.SeparateQueryFromModifier 9 | { 10 | internal class SeparateQueryFromModifier 11 | { 12 | public void CheckSecurity(string[] people) 13 | { 14 | this.SendAlert(people); 15 | string found = FoundPerson(people); 16 | SomeLaterCode(found); 17 | } 18 | 19 | private void SomeLaterCode(string found) 20 | { 21 | throw new NotImplementedException(); 22 | } 23 | 24 | public void SendAlert(string[] people) 25 | { 26 | if (!FoundPerson(people).Equals("")) 27 | { 28 | this.SendAlert(); 29 | } 30 | } 31 | 32 | public string FoundPerson(string[] people) 33 | { 34 | for (int i = 0; i < people.Length; i++) 35 | { 36 | if (people[i].Equals("Don")) 37 | { 38 | SendAlert(); 39 | return "Don"; 40 | } 41 | if (people[i].Equals("John")) 42 | { 43 | SendAlert(); 44 | return "John"; 45 | } 46 | } 47 | return ""; 48 | } 49 | 50 | private void SendAlert() 51 | { 52 | throw new NotImplementedException(); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /RefactorPractice/MovingFeaturesBetweenObjects/ExtractClass/ExtractClass.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace RefactorPractice.MovingFeaturesBetweenObjects.ExtractClass 7 | { 8 | public class ExtractClass 9 | { 10 | public class Person 11 | { 12 | private TelephoneNumber _officeTelephone = new TelephoneNumber(); 13 | 14 | public string Name { get; set; } 15 | 16 | public string GetOfficeTelephoneNumber() 17 | { 18 | return $"({this._officeTelephone.AreaCode}) {this._officeTelephone.Number}"; 19 | } 20 | } 21 | 22 | public class TelephoneNumber 23 | { 24 | public string AreaCode { get; set; } 25 | 26 | public string Number { get; set; } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /RefactorPractice/MovingFeaturesBetweenObjects/ExtractClass/README.md: -------------------------------------------------------------------------------- 1 | ## 提煉類別(Extract Class) 2 | 3 | 某個Class做了應該由兩個類別做的事,建立一個新類別,將相關的欄位和函式從舊Class搬到新Class 4 | 5 | ## 優點: 6 | * Class之間的職責更為清楚 7 | * 分解成小Class提高了可維護性 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /RefactorPractice/MovingFeaturesBetweenObjects/HideDelegate/HideDelegate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace RefactorPractice.MovingFeaturesBetweenObjects.HideDelegate 7 | { 8 | public class HideDelegate 9 | { 10 | public void Sample() 11 | { 12 | var person = new Person(); 13 | var manager = person.GetManager(); 14 | } 15 | 16 | public class Person 17 | { 18 | public Department Department; 19 | 20 | public Person GetManager() 21 | { 22 | return this.Department.Manager; 23 | } 24 | } 25 | 26 | public class Department 27 | { 28 | public string ChargeCode { get; set; } 29 | 30 | public Person Manager { get; } 31 | 32 | public Department(Person manager) 33 | { 34 | this.Manager = manager; 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /RefactorPractice/MovingFeaturesBetweenObjects/HideDelegate/README.md: -------------------------------------------------------------------------------- 1 | ## 隱藏委託關係(Hide Delegate) 2 | 3 | 用戶端直接呼叫服務物件的委託Class,在服務物件建立用戶端所需的所有函式,隱藏委託關係 4 | (意義就是「封裝」) 5 | 6 | 7 | 8 | ## 優點: 9 | * 每個物件都盡可能的少了解系統的其他部分,一旦發生變化,了解變化的物件會比較少,變化會比較好進行 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /RefactorPractice/MovingFeaturesBetweenObjects/InlineClass/InlineClass.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace RefactorPractice.MovingFeaturesBetweenObjects.InlineClass 7 | { 8 | public class InlineClass 9 | { 10 | public class Person 11 | { 12 | public string Name { get; set; } 13 | 14 | public string Number { get; set; } 15 | 16 | public string AreaCode { get; set; } 17 | 18 | public string GetTelephoneNumber() 19 | { 20 | return $"({this.AreaCode}) {this.Number}"; 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /RefactorPractice/MovingFeaturesBetweenObjects/InlineClass/README.md: -------------------------------------------------------------------------------- 1 | ## 行內類別 (Inline Class) 2 | 3 | 某個Class沒有承擔太多職責,將Class的所有特性搬移到另一個Class中,並移除原本Class 4 | 5 | ## 優點: 6 | * 如果一個Class不再承擔足夠的職責、不再有單獨存在的理由,就將Class搬移並移除。 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /RefactorPractice/MovingFeaturesBetweenObjects/IntroduceForeignMethod/IntroduceForeignMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.MovingFeaturesBetweenObjects.IntroduceForeignMethod 8 | { 9 | public class IntroduceForeignMethod 10 | { 11 | public void Sample() 12 | { 13 | DateTime previousEnd = DateTime.Now; 14 | var newStart = GetNextDay(previousEnd); 15 | } 16 | 17 | private static DateTime GetNextDay(DateTime previousEnd) 18 | { 19 | return new DateTime(previousEnd.Year, previousEnd.Month, previousEnd.Day + 1); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /RefactorPractice/MovingFeaturesBetweenObjects/IntroduceForeignMethod/README.md: -------------------------------------------------------------------------------- 1 | ## 引入外加函式 (Introduce Foreign Method) 2 | 3 | 用戶端程式碼使用的服務類別需要一個額外的函式,但你無法修改這個Class, 4 | 在用戶端的程式碼中建立一個函式,並以服務類別實體作為參數。 5 | 6 | ## 優點: 7 | * 這個函式最好的方式是在服務類別中建立,有時考量到程式碼擁有權的問題,才需要使用這個原則 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /RefactorPractice/MovingFeaturesBetweenObjects/IntroduceLocalExtension/README.md: -------------------------------------------------------------------------------- 1 | ## 引入區域性擴展 (Introduce Local Extension) 2 | 3 | 你所使用的服務類別需要一些額外的函式,但你無法修改這個Class, 4 | 建立一個新的Class,使他包含這些額外的函式。 5 | 讓這個擴展品成為來源Class的子類別(SubClass)或Wrapper(外覆類別) 6 | 7 | ## 優點: 8 | * 擴展類別不一定和來源類別有強烈的繼承關係,只要能夠提供所有特性即可 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /RefactorPractice/MovingFeaturesBetweenObjects/MoveField/MoveField.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace RefactorPractice.MovingFeaturesBetweenObjects.MoveField 7 | { 8 | public class MoveField 9 | { 10 | public class Account 11 | { 12 | public AccountType _type; 13 | 14 | public double InterestForAmonuntDays(double amount, int days) 15 | { 16 | return this._type.InterestRate * amount * days / 365; 17 | } 18 | } 19 | 20 | public class AccountType 21 | { 22 | public double InterestRate { get; set; } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /RefactorPractice/MovingFeaturesBetweenObjects/MoveField/MoveFieldWithSelfEncapsulation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace RefactorPractice.MovingFeaturesBetweenObjects.MoveField 7 | { 8 | public class MoveFieldWithSelfEncapsulation 9 | { 10 | public class Account 11 | { 12 | public AccountType _type; 13 | 14 | public double InterestRate 15 | { 16 | get 17 | { 18 | return this._type.InterestRate; 19 | } 20 | set 21 | { 22 | this._type.InterestRate = value; 23 | } 24 | } 25 | 26 | public double InterestForAmonuntDays(double amount, int days) 27 | { 28 | return this.InterestRate * amount * days / 365; 29 | } 30 | } 31 | 32 | public class AccountType 33 | { 34 | public double InterestRate { get; set; } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /RefactorPractice/MovingFeaturesBetweenObjects/MoveField/README.md: -------------------------------------------------------------------------------- 1 | ## 移動欄位(Move Field) 2 | 3 | 某個欄位被所在的Class的以外Class引用, 4 | 在目標Class建立一個新的Field,修改所有引用點至新的Field 5 | 6 | ## 優點: 7 | * 如果發現別的Class更多的引用這個欄位,就移動它。 8 | -------------------------------------------------------------------------------- /RefactorPractice/MovingFeaturesBetweenObjects/MoveMethod/MoveMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace RefactorPractice.MovingFeaturesBetweenObjects.MoveMethod 7 | { 8 | public class MoveMethod 9 | { 10 | public class Account 11 | { 12 | private AccountType _type; 13 | 14 | private int _daysOverdrawn; 15 | 16 | public double BankCharge() 17 | { 18 | double result = 4.5; 19 | if (this._daysOverdrawn > 0) 20 | { 21 | result += this._type.OverDraftCharge(this); 22 | } 23 | return result; 24 | } 25 | 26 | public int GetDaysOverdrawn() 27 | { 28 | return this._daysOverdrawn; 29 | } 30 | } 31 | 32 | public class AccountType 33 | { 34 | public bool IsPremium() 35 | { 36 | return true; 37 | } 38 | 39 | public double OverDraftCharge(Account account) 40 | { 41 | if (this.IsPremium()) 42 | { 43 | double result = 10; 44 | if (account.GetDaysOverdrawn() > 7) 45 | { 46 | result += (account.GetDaysOverdrawn() - 7) * 0.85; 47 | } 48 | return result; 49 | } 50 | else 51 | { 52 | return account.GetDaysOverdrawn() * 1.75; 53 | } 54 | } 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /RefactorPractice/MovingFeaturesBetweenObjects/MoveMethod/README.md: -------------------------------------------------------------------------------- 1 | ## 移動函式(Move Method) 2 | 3 | 有個函式與其所駐的Class與另一個Class進行更多交流,呼叫後者或被後者呼叫。 4 | 5 | 在該函式最常引用的Class中建立一個有著類似行為的新函式。 6 | 將舊函式變成一個單純的委託函式,或是將舊函式完全移除 7 | 8 | ## 優點: 9 | * 如果一個class與另一個class有太多的合作,而形成高度耦合, 10 | 透過移動函式,讓class變得更簡單。 11 | -------------------------------------------------------------------------------- /RefactorPractice/MovingFeaturesBetweenObjects/RemoveMiddleMan/README.md: -------------------------------------------------------------------------------- 1 | ## 移除中間人(Remove Middle Man) 2 | 3 | 某個Class使用了過多的簡單委託動作,直接讓用戶端呼叫受託類別(delegate) 4 | 5 | ## 優點: 6 | * 一旦簡單委託動作過多,會增加程式碼複雜度,服務物件會完全變成一個中間人,這時候直接讓用戶端呼叫受託類別即可。 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /RefactorPractice/MovingFeaturesBetweenObjects/RemoveMiddleMan/RemoveMiddleMan.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace RefactorPractice.MovingFeaturesBetweenObjects.RemoveMiddleMan 7 | { 8 | public class RemoveMiddleMan 9 | { 10 | public void Sample() 11 | { 12 | var person = new Person(); 13 | var manager = person.Department.Manager; 14 | } 15 | 16 | public class Person 17 | { 18 | public Department Department; 19 | } 20 | 21 | public class Department 22 | { 23 | public string ChargeCode { get; set; } 24 | 25 | public Person Manager { get; } 26 | 27 | public Department(Person manager) 28 | { 29 | this.Manager = manager; 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/ChangeBidirectonalAssociationToUnidirectional/ChangeBidirectonalAssociationToUnidirectional.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.OrganizingData.ChangeBidirectonalAssociationToUnidirectional 8 | { 9 | public class ChangeBidirectonalAssociationToUnidirectional 10 | { 11 | public class Order 12 | { 13 | public Customer Customer { get; set; } 14 | 15 | public void SetCustomer(Customer arg) 16 | { 17 | if (Customer != null) 18 | { 19 | Customer.FriendOrders().Remove(this); 20 | } 21 | 22 | Customer = arg; 23 | if (Customer != null) 24 | { 25 | Customer.FriendOrders().Add(this); 26 | } 27 | } 28 | 29 | public double GetDiscountedPrice(Customer customer) 30 | { 31 | return GetGrossPirce() * (1 - customer.GetDiscount()); 32 | } 33 | 34 | private double GetGrossPirce() 35 | { 36 | throw new NotImplementedException(); 37 | } 38 | } 39 | 40 | public class Customer 41 | { 42 | private List _orders = new List(); 43 | 44 | public List FriendOrders() 45 | { 46 | return this._orders; 47 | } 48 | 49 | public double GetDiscount() 50 | { 51 | throw new NotImplementedException(); 52 | } 53 | 54 | public double GetPriceFor(Order order) 55 | { 56 | return order.GetDiscountedPrice(this); 57 | } 58 | 59 | private void AddOrder(Order arg) 60 | { 61 | arg.SetCustomer(this); 62 | } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/ChangeBidirectonalAssociationToUnidirectional/README.md: -------------------------------------------------------------------------------- 1 | ## 將雙向關聯改為單向 (Change Bidirectonal Association To Unidirectional) 2 | 3 | 兩個Class之間有雙向關聯,當其中一個Class不再需要另一個Class的特性。 4 | 5 | 就去除不必要的關聯。 6 | 7 | ## 優點: 8 | * 雙向關聯必須確保物件能正確的被創建與刪除 9 | * 大量的雙向關聯也容易產生殭屍物件 10 | * 雙向關聯迫使兩個Class之間有相依性,過多的相依性會造成緊耦合。 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/ChangeReferenceToValue/ChangeReferenceToValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.OrganizingData.ChangeReferenceToValue 8 | { 9 | public class ChangeReferenceToValue 10 | { 11 | private void Sample() 12 | { 13 | var instance = Activator.CreateInstance(typeof(Currency), "USD"); 14 | } 15 | 16 | public class Currency 17 | { 18 | private string Code { get; set; } 19 | 20 | private Currency(string code) 21 | { 22 | this.Code = code; 23 | } 24 | 25 | public override bool Equals(object obj) 26 | { 27 | if (!(obj is Currency)) 28 | { 29 | return false; 30 | } 31 | return Code.Equals(((Currency)obj).Code); 32 | } 33 | 34 | public override int GetHashCode() 35 | { 36 | return Code.GetHashCode(); 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/ChangeReferenceToValue/README.md: -------------------------------------------------------------------------------- 1 | ## 將參考物件改為實值物件 (Change Reference To Value) 2 | 3 | 有一個參考物件,很小且不太會變動,而且不易管理。 4 | 5 | 將它變成一個實值物件 6 | 7 | ## 優點: 8 | * 在分布系統和併行系統中,使用實值物件就不需要考慮同步的問題 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/ChangeUnidirectionalAssociationToBidirectonal/ChangeUnidirectionalAssociationToBidirectonal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.OrganizingData.ChangeUnidirectionalAssociationToBidirectonal 8 | { 9 | public class ChangeUnidirectionalAssociationToBidirectonal 10 | { 11 | public class Order 12 | { 13 | public Customer Customer { get; set; } 14 | 15 | public void SetCustomer(Customer arg) 16 | { 17 | if (Customer != null) 18 | { 19 | Customer.FriendOrders().Remove(this); 20 | } 21 | 22 | this.Customer = arg; 23 | 24 | if (Customer != null) 25 | { 26 | Customer.FriendOrders().Add(this); 27 | } 28 | } 29 | } 30 | 31 | public class Customer 32 | { 33 | private List _orders { get; set; } 34 | 35 | public List FriendOrders() 36 | { 37 | return _orders; 38 | } 39 | 40 | public void AddOrder(Order arg) 41 | { 42 | arg.SetCustomer(this); 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/ChangeUnidirectionalAssociationToBidirectonal/README.md: -------------------------------------------------------------------------------- 1 | ## 將單向關聯改為雙向 (Change Unidirectional Association To Bidirectonal) 2 | 3 | 兩個Class都需要用到對方的特性,但其中只有一條單向連結。 4 | 5 | 增加一個反向指標,並修改函式能夠同時更新兩條連結。 6 | 7 | ## 優點: 8 | * 當一個參考物件需要知道引用物件的某些特性來進行操作,就需要建立雙向引用關係 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/ChangeValueToReference/ChangeValueToReference.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.CompilerServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace RefactorPractice.OrganizingData.ChangeValueToReference 9 | { 10 | public class ChangeValueToReference 11 | { 12 | public class Customer 13 | { 14 | private static Dictionary _instances = new Dictionary(); 15 | public string Name { get; set; } 16 | 17 | private Customer(string name) 18 | { 19 | this.Name = name; 20 | } 21 | 22 | public static void LoadCustomers() 23 | { 24 | new Customer("Lemon Car Hire").Store(); 25 | new Customer("Associated Coffee Machines").Store(); 26 | new Customer("Bilston Gasworks").Store(); 27 | } 28 | 29 | public static Customer Create(string name) 30 | { 31 | return _instances[name] as Customer; 32 | } 33 | 34 | private void Store() 35 | { 36 | _instances.Add(this.Name, this); 37 | } 38 | } 39 | 40 | public class Order 41 | { 42 | public Customer Customer { get; set; } 43 | 44 | public Order(string customerName) 45 | { 46 | this.Customer = Customer.Create(customerName); 47 | } 48 | 49 | public static int NumberOfOrdersFor(List orders, string customer) 50 | { 51 | var result = 0; 52 | foreach (var order in orders) 53 | { 54 | if (order.GetCusomterName().Equals(customer)) 55 | { 56 | result++; 57 | } 58 | } 59 | return result; 60 | } 61 | 62 | public string GetCusomterName() 63 | { 64 | return this.Customer.Name; 65 | } 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/ChangeValueToReference/README.md: -------------------------------------------------------------------------------- 1 | ## 將實值物件改為參考物件 (Change Value To Reference) 2 | 3 | 有一個Class,衍生出許多相等實體,你希望將他們替換成單一物件。 4 | 5 | 將這個實值物件變成一個參考物件 6 | 7 | ## 優點: 8 | * 可以給這個物件加入一些資料,又可以確保所有引用的地方會一併修改。 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/DuplicateObservedData/README.md: -------------------------------------------------------------------------------- 1 | ## 複製被監視的資料 (Duplicate Observed Data) 2 | 3 | 有一些Domain Data在GUI介面中,而Domain Method需要使用。 4 | 5 | 將該筆資料複製到一個Domain Object中。建立一個Observer範式,用以對Domain Object和GUI內的重複資料進行同步 6 | 7 | ## 優點: 8 | * 同一筆資料有可能需要於GUI中,也可能在Domain Model中,所以還必須提供相對應的同步機制。 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/EncapsulateCollection/README.md: -------------------------------------------------------------------------------- 1 | ## 封裝群集(Encapsulate Collection) 2 | 3 | 有個函式回傳一個群集(Collection)。 4 | 5 | 讓這個函式傳回該群集的一個唯獨映件,並在這個Class中提供添加/移除(add/remove)群集元素的函式 6 | 7 | ## 優點: 8 | * 在C#中,就是泛型集合的實作 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/EncapsulateField/EncapsulateField.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace RefactorPractice.OrganizingData.EncapsulateField 9 | { 10 | public class EncapsulateField 11 | { 12 | private string _name; 13 | 14 | public string Name 15 | { 16 | get 17 | { 18 | return this._name; 19 | } 20 | set 21 | { 22 | this._name = value; 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/EncapsulateField/README.md: -------------------------------------------------------------------------------- 1 | ## 封裝欄位 (EncapsulateField) 2 | 3 | 你的Class存在一個public欄位。 4 | 5 | 將它宣告成private,並提供相對應的存取函式 6 | 7 | ## 優點: 8 | * 物件導向的首要原則就是封裝,又稱為資料隱藏。如果資料開放成public, 9 | 那任何使用到的物件都可以隨意修改資料,會造成資料與行為不一致。 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/ReplaceArrayWithObject/README.md: -------------------------------------------------------------------------------- 1 | ## 以物件取代陣列 (Replace Array With Objects) 2 | 3 | 有一個陣列,其中元素各自代表不同東西。 4 | 5 | 以物件替換陣列。對於陣列中每個元素,以一個欄位表示。 6 | 7 | ## 優點: 8 | * 陣列應該只用於某種順序容納的一組相似物件。 9 | 如果一個陣列容納了很多物件,會給程式碼閱讀者帶來麻煩。 10 | 而物件有欄位名稱與函式可以詮釋,還可以將資訊加以封裝。 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/ReplaceArrayWithObject/ReplaceArrayWithObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.OrganizingData.ReplaceArrayWithObject 8 | { 9 | public class ReplaceArrayWithObject 10 | { 11 | public void Sample() 12 | { 13 | Performance row = new Performance(); 14 | row.Name = "Liverpool"; 15 | row.Wins = 15; 16 | int wins = row.Wins; 17 | } 18 | 19 | public class Performance 20 | { 21 | public string Name { get; set; } 22 | 23 | public int Wins { get; set; } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/ReplaceDataValueWithObject/README.md: -------------------------------------------------------------------------------- 1 | ## 以物件取代資料值 (Replace Data Value with Object) 2 | 3 | 你有一個資料項,需要額外的行為和資料。 4 | 將這個資料項變成物件 5 | 6 | ## 優點: 7 | * 以往用簡單的資料項來表示簡單的行為,隨著開發的進行,會出現不夠用的情況,這時候就需要將資料值變成物件。 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/ReplaceDataValueWithObject/ReplaceDataValueWithObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.OrganizingData.ReplaceDataValueWithObject 8 | { 9 | public class ReplaceDataValueWithObject 10 | { 11 | public class Order 12 | { 13 | private Customer _customer; 14 | 15 | public Order(string customerName) 16 | { 17 | this._customer = new Customer(customerName); 18 | } 19 | 20 | public string GetCustomerName() 21 | { 22 | return this._customer.Name; 23 | } 24 | 25 | private static int NumberOfOrderFor(List orders, Customer customer) 26 | { 27 | var result = 0; 28 | foreach (var order in orders) 29 | { 30 | if (order._customer.Equals(customer)) 31 | { 32 | result++; 33 | } 34 | } 35 | return result; 36 | } 37 | } 38 | 39 | public class Customer 40 | { 41 | public string Name { get; set; } 42 | 43 | public Customer(string name) 44 | { 45 | this.Name = name; 46 | } 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/ReplaceMagicNumberWithSymbolicConstant/README.md: -------------------------------------------------------------------------------- 1 | ## 以符號常數(字面常數)取代魔術數字 (Replace Magic Number With Symbolic Constant) 2 | 3 | 有一個字面數值,帶有特別含意。 4 | 5 | 創造一個常數,根據其意義命名,並將上述的字面數值替換成這個常數 6 | 7 | ## 優點: 8 | * 魔術數字是指有特殊意義,但又不能明確表達意義的數字。 9 | 用有意義的常數取代,會大幅提升可讀性。 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/ReplaceMagicNumberWithSymbolicConstant/ReplaceMagicNumberWithSymbolicConstant.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.OrganizingData.ReplaceMagicNumberWithSymbolicConstant 8 | { 9 | public class ReplaceMagicNumberWithSymbolicConstant 10 | { 11 | public const double GRAVITATIONAL_CONSTANT = 9.81; 12 | 13 | public double PotentialEnergy(double mass, double height) 14 | { 15 | return mass * GRAVITATIONAL_CONSTANT * height; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/ReplaceRecordWithDataClass/README.md: -------------------------------------------------------------------------------- 1 | ## 以資料類別取代紀錄(Replace Record With Data Class) 2 | 3 | 你需要面對傳統編程環境中的Record Structure。 4 | 5 | 為該record(紀錄)創建一個啞資料物件(dumb data object) 6 | 7 | ## 優點: 8 | * Record Structure是許多編程環境的共同性質 9 | ,在物件導向的語言中用Data object替換他們。 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/ReplaceSubclassWithFields/README.md: -------------------------------------------------------------------------------- 1 | ## 以欄位取代子類別(Replace Subclass With Fields) 2 | 3 | 各個subclasses的差別在於傳回常數的函式身上。 4 | 5 | 修改這些函式,使他們傳回superclass中的某個(新增欄位),然後銷毀subclass 6 | 7 | ## 優點: 8 | * 若subclass中只有常數函式,沒有足夠的存在價值,可以在superclass中設計一個與常數函式回傳值相應的欄位, 9 | 從而完全去除這樣的subclass。 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/ReplaceSubclassWithFields/ReplaceSubclassWithFields.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net.Http.Headers; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | using RefactorPractice.MovingFeaturesBetweenObjects.ExtractClass; 9 | 10 | namespace RefactorPractice.OrganizingData.ReplaceSubclassWithFields 11 | { 12 | internal class ReplaceSubclassWithFields 13 | { 14 | public class Person 15 | { 16 | private bool _isMale; 17 | 18 | private char _code; 19 | 20 | protected Person(bool isMale, char code) 21 | { 22 | this._isMale = isMale; 23 | this._code = code; 24 | } 25 | 26 | public bool IsMale() 27 | { 28 | return this._isMale; 29 | } 30 | 31 | public char GetCode() 32 | { 33 | return this._code; 34 | } 35 | 36 | public static Person CreateFemale() 37 | { 38 | return new Person(true, 'M'); 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/ReplaceTypeCodeWithClass/README.md: -------------------------------------------------------------------------------- 1 | ## 以類別取代型別代碼(Replace TypeCode With Class) 2 | 3 | Class之中有一個數值型別代碼(numberic type code),但它不影響class行為。 4 | 5 | 以一個新的class替換該數值型別代碼(type code) 6 | 7 | ## 優點: 8 | * 符號名稱只是別名,編譯器會進行型別檢驗的還是背後的那個數值。 9 | 任何會接受type code作為引數的函式,所期望的還是一個數值,無法強制使用符號名稱,這會降低程式可讀性 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/ReplaceTypeCodeWithClass/ReplaceTypeCodeWithClass.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.OrganizingData.ReplaceTypeCodeWithClass 8 | { 9 | public class ReplaceTypeCodeWithClass 10 | { 11 | public void Sample() 12 | { 13 | Person thePerson = new Person(BloodGroup.A); 14 | var code = thePerson.BloodGroup.Code; 15 | thePerson.BloodGroup = BloodGroup.AB; 16 | } 17 | 18 | public class Person 19 | { 20 | private BloodGroup _bloodGroup; 21 | 22 | public BloodGroup BloodGroup 23 | { 24 | get 25 | { 26 | return this._bloodGroup; 27 | } 28 | 29 | set 30 | { 31 | this._bloodGroup = value; 32 | } 33 | } 34 | 35 | public Person(BloodGroup bloodGroup) 36 | { 37 | this._bloodGroup = bloodGroup; 38 | } 39 | } 40 | 41 | public class BloodGroup 42 | { 43 | public static BloodGroup O = new BloodGroup(0); 44 | 45 | public static BloodGroup A = new BloodGroup(1); 46 | 47 | public static BloodGroup B = new BloodGroup(2); 48 | 49 | public static BloodGroup AB = new BloodGroup(3); 50 | 51 | private static BloodGroup[] _values = { O, A, B, AB }; 52 | 53 | private int _code; 54 | 55 | public int Code 56 | { 57 | get 58 | { 59 | return this._code; 60 | } 61 | } 62 | 63 | public BloodGroup(int code) 64 | { 65 | this._code = code; 66 | } 67 | 68 | private static BloodGroup GetCode(int arg) 69 | { 70 | return _values[arg]; 71 | } 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/ReplaceTypeCodeWithStateOrStrategy/README.md: -------------------------------------------------------------------------------- 1 | ## 以State/Strategy取代型別代碼(Replace Type Code With State/Strategy) 2 | 3 | 有一個type code,它會影響class的行為,但你無法使用subclassing。 4 | 5 | 以state object(專門描述狀態的物件)取代type code 6 | 7 | 8 | ## 優點: 9 | * 如果type code的值在物件生命週期中發生變化,或其他原因使得宿主類別不能被subclassing,就使用這個原則。 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/ReplaceTypeCodeWithStateOrStrategy/ReplaceTypeCodeWithStateOrStrategy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Linq; 5 | using System.Net.Http.Headers; 6 | using System.Net.NetworkInformation; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace RefactorPractice.OrganizingData.ReplaceTypeCodeWithStateOrStrategy 11 | { 12 | public class ReplaceTypeCodeWithStateOrStrategy 13 | { 14 | public class Employee 15 | { 16 | private EmployeeType _type; 17 | 18 | private int _monthlySalary; 19 | 20 | private int _commission; 21 | 22 | private int _bonus; 23 | 24 | public int Type 25 | { 26 | get 27 | { 28 | return this._type.GetTypeCode(); 29 | } 30 | set 31 | { 32 | switch (value) 33 | { 34 | case EmployeeType.ENGINEER: 35 | this._type = new Enginer(); 36 | break; 37 | 38 | case EmployeeType.MANAGER: 39 | this._type = new Manager(); 40 | break; 41 | 42 | case EmployeeType.SALESMAN: 43 | this._type = new SalesMan(); 44 | break; 45 | 46 | default: 47 | throw new ArgumentException(); 48 | } 49 | } 50 | } 51 | 52 | public Employee(int type) 53 | { 54 | this._type = EmployeeType.NewType(type); 55 | } 56 | 57 | private int PayAmount() 58 | { 59 | switch (this.Type) 60 | { 61 | case EmployeeType.ENGINEER: return _monthlySalary; 62 | case EmployeeType.SALESMAN: return _monthlySalary * _commission; 63 | case EmployeeType.MANAGER: return _monthlySalary * _bonus; 64 | default: 65 | throw new ArgumentException(); 66 | } 67 | } 68 | } 69 | 70 | public class Enginer : EmployeeType 71 | { 72 | public override int GetTypeCode() 73 | { 74 | return ENGINEER; 75 | } 76 | } 77 | 78 | public class Manager : EmployeeType 79 | { 80 | public override int GetTypeCode() 81 | { 82 | return MANAGER; 83 | } 84 | } 85 | 86 | public class SalesMan : EmployeeType 87 | { 88 | public override int GetTypeCode() 89 | { 90 | return SALESMAN; 91 | } 92 | } 93 | 94 | public abstract class EmployeeType 95 | { 96 | public const int ENGINEER = 0; 97 | 98 | public const int SALESMAN = 1; 99 | 100 | public const int MANAGER = 2; 101 | 102 | public abstract int GetTypeCode(); 103 | 104 | public static EmployeeType NewType(int code) 105 | { 106 | switch (code) 107 | { 108 | case ENGINEER: 109 | return new Enginer(); 110 | 111 | case SALESMAN: 112 | return new SalesMan(); 113 | 114 | case MANAGER: 115 | return new Manager(); 116 | 117 | default: 118 | throw new ArgumentException(); 119 | } 120 | } 121 | } 122 | } 123 | } -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/ReplaceTypeCodeWithSubclasses/README.md: -------------------------------------------------------------------------------- 1 | ## 以子類別取代型別代碼(Replace Type Code With Subclasses) 2 | 3 | 有一個不變的type code,它會影響class的行為。 4 | 5 | 以一個subclass取代這個type code。 6 | 7 | ## 優點: 8 | * 如果你面對的type code會影響其宿主類別的行為,最好的辦法就是借助多型來處理變化行為。 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/ReplaceTypeCodeWithSubclasses/ReplaceTypeCodeWithSubclasses.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.OrganizingData.ReplaceTypeCodeWithSubclasses 8 | { 9 | internal class ReplaceTypeCodeWithSubclasses 10 | { 11 | public class Employee 12 | { 13 | private int _type; 14 | 15 | public int Type 16 | { 17 | get 18 | { 19 | return this._type; 20 | } 21 | } 22 | 23 | public const int ENGINEER = 0; 24 | 25 | public const int SALESMAN = 1; 26 | 27 | public const int MANAGER = 2; 28 | 29 | protected Employee(int type) 30 | { 31 | this._type = type; 32 | } 33 | 34 | public static Employee Create(int type) 35 | { 36 | switch (type) 37 | { 38 | case Employee.ENGINEER: return new Engineer(); 39 | case Employee.MANAGER: return new Manager(); 40 | case Employee.SALESMAN: return new SalesMan(); 41 | default: throw new ArgumentException(); 42 | } 43 | } 44 | } 45 | 46 | public class Engineer : Employee 47 | { 48 | public Engineer() : base(Employee.ENGINEER) 49 | { 50 | } 51 | 52 | public int Type 53 | { 54 | get 55 | { 56 | return Employee.ENGINEER; 57 | } 58 | } 59 | } 60 | 61 | public class Manager : Employee 62 | { 63 | public Manager() : base(Employee.MANAGER) 64 | { 65 | } 66 | 67 | public int Type 68 | { 69 | get 70 | { 71 | return Employee.MANAGER; 72 | } 73 | } 74 | } 75 | 76 | public class SalesMan : Employee 77 | { 78 | public SalesMan() : base(Employee.SALESMAN) 79 | { 80 | } 81 | 82 | public int Type 83 | { 84 | get 85 | { 86 | return Employee.SALESMAN; 87 | } 88 | } 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/SelfEncapsulateField/README.md: -------------------------------------------------------------------------------- 1 | ## 自我封裝欄位 (Self Encapsulate Field) 2 | 3 | 你直接存取一個欄位,但與欄位之間耦合關係逐漸加重。 4 | 為這個欄位建立取值/設值(get/set),並只以這些函式來存取欄位。 5 | 6 | ## 優點: 7 | * 直接存取欄位的程式碼比較好閱讀 8 | * 間接存取欄位的好處是,可以覆寫一個函式而改變取得資料的途徑。 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/OrganizingData/SelfEncapsulateField/SelfEncapsulateField.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.OrganizingData.SelfEncapsulateField 8 | { 9 | public class SelfEncapsulateField 10 | { 11 | public class IntRange 12 | { 13 | public int Low { get; set; } 14 | 15 | public int High { get; set; } 16 | 17 | public IntRange(int low, int high) 18 | { 19 | this.Low = low; 20 | this.High = high; 21 | } 22 | 23 | public bool Includes(int arg) 24 | { 25 | return arg >= this.Low && arg <= this.High; 26 | } 27 | 28 | public void Grow(int factor) 29 | { 30 | this.High = this.High * factor; 31 | } 32 | } 33 | 34 | public class CappedRange : IntRange 35 | { 36 | public int Cap; 37 | 38 | public int High { get { return Math.Min(base.High, this.Cap); } } 39 | 40 | public CappedRange(int low, int high, int cap) : base(low, high) 41 | { 42 | this.Cap = cap; 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /RefactorPractice/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using RefatorPractice.Ch1Lab; 5 | 6 | namespace RefatorPractice 7 | { 8 | internal class Program 9 | { 10 | private static void Main(string[] args) 11 | { 12 | var customer = new Customer("Bryan"); 13 | var rental = new Rental(new Movie("Movie1", 0), 2); 14 | customer.AddRental(rental); 15 | var result = customer.GetStatement(); 16 | Console.WriteLine(result); 17 | Console.Read(); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /RefactorPractice/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 組件的一般資訊是由下列的屬性集控制。 6 | // 變更這些屬性的值即可修改組件的相關 7 | // 資訊。 8 | [assembly: AssemblyTitle("RefactorPractice")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("RefactorPractice")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 將 ComVisible 設為 false 可對 COM 元件隱藏 18 | // 組件中的類型。若必須從 COM 存取此組件中的類型, 19 | // 的類型,請在該類型上將 ComVisible 屬性設定為 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 下列 GUID 為專案公開 (Expose) 至 COM 時所要使用的 typelib ID 23 | [assembly: Guid("dc520388-fbcd-4a58-98bc-ebc9beda2588")] 24 | 25 | // 組件的版本資訊由下列四個值所組成: 26 | // 27 | // 主要版本 28 | // 次要版本 29 | // 組建編號 30 | // 修訂編號 31 | // 32 | // 您可以指定所有的值,或將組建編號或修訂編號設為預設值 33 | // 指定為預設值: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /RefactorPractice/RefactorPractice.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {DC520388-FBCD-4A58-98BC-EBC9BEDA2588} 8 | Exe 9 | RefactorPractice 10 | RefactorPractice 11 | v4.5.2 12 | 512 13 | true 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /RefactorPractice/RefactorPractice.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27004.2009 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RefactorPractice", "RefactorPractice.csproj", "{DC520388-FBCD-4A58-98BC-EBC9BEDA2588}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RefactorPractice.UnitTest", "..\RefactorPractice.UnitTest\RefactorPractice.UnitTest.csproj", "{65544821-9A45-4209-900D-CE53136B51D8}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{55D9128D-433F-40A1-9061-91B261BB6E93}" 11 | ProjectSection(SolutionItems) = preProject 12 | ..\README.md = ..\README.md 13 | EndProjectSection 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|Any CPU = Debug|Any CPU 18 | Release|Any CPU = Release|Any CPU 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {DC520388-FBCD-4A58-98BC-EBC9BEDA2588}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {DC520388-FBCD-4A58-98BC-EBC9BEDA2588}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {DC520388-FBCD-4A58-98BC-EBC9BEDA2588}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {DC520388-FBCD-4A58-98BC-EBC9BEDA2588}.Release|Any CPU.Build.0 = Release|Any CPU 25 | {65544821-9A45-4209-900D-CE53136B51D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {65544821-9A45-4209-900D-CE53136B51D8}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {65544821-9A45-4209-900D-CE53136B51D8}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {65544821-9A45-4209-900D-CE53136B51D8}.Release|Any CPU.Build.0 = Release|Any CPU 29 | EndGlobalSection 30 | GlobalSection(SolutionProperties) = preSolution 31 | HideSolutionNode = FALSE 32 | EndGlobalSection 33 | GlobalSection(ExtensibilityGlobals) = postSolution 34 | SolutionGuid = {11FF58F1-48FE-48E9-B6D4-544096BAAE07} 35 | EndGlobalSection 36 | EndGlobal 37 | -------------------------------------------------------------------------------- /RefactorPractice/SimplifyingConditionalExpressions/ConsolidateConditionalExpression/ConsolidateConditionalExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.SimplifyingConditionalExpressions.ConsolidateConditionalExpression 8 | { 9 | internal class ConsolidateConditionalExpression 10 | { 11 | public class LogicOR 12 | { 13 | private int _seniority; 14 | 15 | private int _monthsDisabled; 16 | 17 | private bool _isPartTime; 18 | 19 | private double DisabilityAmount() 20 | { 21 | if (IsNotEligibleForDisability()) 22 | { 23 | return 0; 24 | } 25 | 26 | return 1; 27 | } 28 | 29 | private bool IsNotEligibleForDisability() 30 | { 31 | return _seniority < 2 || _monthsDisabled > 12 || _isPartTime; 32 | } 33 | } 34 | 35 | public class LogicANDs 36 | { 37 | public double Sample() 38 | { 39 | return this.OnVacation() && this.LengthOfService() > 10 ? 1 : 0.5; 40 | } 41 | 42 | private int LengthOfService() 43 | { 44 | throw new NotImplementedException(); 45 | } 46 | 47 | private bool OnVacation() 48 | { 49 | throw new NotImplementedException(); 50 | } 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /RefactorPractice/SimplifyingConditionalExpressions/ConsolidateConditionalExpression/README.md: -------------------------------------------------------------------------------- 1 | ## 合併條件式(Consolidate Conditional Expression) 2 | 3 | 有一系列的條件測試,都得到相同的結果 4 | 5 | 將這些測試合併為一個條件式,並將這個條件式提煉成一個函式 6 | 7 | ## 優點: 8 | * 可以讓一次檢查的用意更清晰。 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/SimplifyingConditionalExpressions/ConsolidateDuplicateConditionalFragments/ConsolidateDuplicateConditionalFragments.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.SimplifyingConditionalExpressions.ConsolidateDuplicateConditionalFragments 8 | { 9 | internal class ConsolidateDuplicateConditionalFragments 10 | { 11 | public void Sample() 12 | { 13 | var total = 0.0D; 14 | var price = 0.0D; 15 | if (IsSpecialDeal()) 16 | { 17 | total = price * 0.95; 18 | } 19 | else 20 | { 21 | total = price * 0.98; 22 | } 23 | this.Send(); 24 | } 25 | 26 | private bool IsSpecialDeal() 27 | { 28 | throw new NotImplementedException(); 29 | } 30 | 31 | private void Send() 32 | { 33 | throw new NotImplementedException(); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /RefactorPractice/SimplifyingConditionalExpressions/ConsolidateDuplicateConditionalFragments/README.md: -------------------------------------------------------------------------------- 1 | ## 合併重複的條件片段(Consolidate Duplicate Conditional Fragments) 2 | 3 | 在這個條件式的每個分支上有著相同的一段程式碼。 4 | 5 | 將這段重複的程式碼搬移到條件式之外 6 | 7 | ## 優點: 8 | * 如果發現條件式分支內都執行了相同的某段程式碼,應該要把這段程式碼搬移到條件式外, 9 | 這樣才能清楚表達那些東西是會變化,那些不會。 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/SimplifyingConditionalExpressions/DecomposeConditional/DecomposeConditional.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.SimplifyingConditionalExpressions.DecomposeConditional 8 | { 9 | internal class DecomposeConditional 10 | { 11 | private double _winterRate; 12 | 13 | private double _winterServiceCharge; 14 | 15 | private double _summerRate; 16 | 17 | public void Sample() 18 | { 19 | double charge = 0.0D; 20 | double quantity = 0.0D; 21 | 22 | if (NotSummer(DateTime.Now)) 23 | { 24 | charge = SummerCharge(quantity); 25 | } 26 | else 27 | { 28 | charge = WinterCharge(quantity); 29 | } 30 | } 31 | 32 | private double WinterCharge(double quantity) 33 | { 34 | return quantity * _summerRate; 35 | } 36 | 37 | private double SummerCharge(double quantity) 38 | { 39 | return quantity * _winterRate + _winterServiceCharge; 40 | } 41 | 42 | private static bool NotSummer(DateTime now) 43 | { 44 | DateTime SUMMER_START = new DateTime(); 45 | DateTime SUMMER_END = new DateTime(); 46 | return now <= SUMMER_START || now >= SUMMER_END; 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /RefactorPractice/SimplifyingConditionalExpressions/DecomposeConditional/README.md: -------------------------------------------------------------------------------- 1 | ## 分解條件式(Decompose Conditional) 2 | 3 | 有一個複雜條件式(if then else)。 4 | 5 | 從if、then、else三個段落中分別提煉獨立函式 6 | 7 | ## 優點: 8 | * 複雜的條件邏輯是最常導致複雜度上升的原因,可以將它分解成多個獨立函式,並為新函式命名,能夠表達函式的意思 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/SimplifyingConditionalExpressions/IntroduceAssertion/README.md: -------------------------------------------------------------------------------- 1 | ## 引入斷言(Introduce Assertion) 2 | 3 | 某一段程式碼需要對狀態做出某種假設 4 | 5 | 以Assertion(斷言)明確表現這種假設 6 | 7 | ## 優點: 8 | * Assertion是一個條件式,應該總是為真。如果他失敗,代表程式有發生問題。 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/SimplifyingConditionalExpressions/IntroduceNullObject/IntroduceNullObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.SimplifyingConditionalExpressions.IntroduceNullObject 8 | { 9 | internal class IntroduceNullObject 10 | { 11 | public void Sample() 12 | { 13 | Customer customer = (new Site()).Customer; 14 | BillingPlan plan = new BillingPlan(); 15 | customer.Plan = plan; 16 | var customerName = customer.Name; 17 | int weeksDelinquent = customer.History.GetWeeksDelinquentInLastYear(); 18 | } 19 | 20 | public class Site 21 | { 22 | private Customer _customer; 23 | 24 | public Customer Customer 25 | { 26 | get 27 | { 28 | return this._customer == null ? Customer.NewNull() : this._customer; 29 | } 30 | } 31 | } 32 | 33 | public class Customer 34 | { 35 | public string Name { get; set; } 36 | 37 | public BillingPlan Plan { get; set; } 38 | 39 | public PaymentHistory History { get; set; } 40 | 41 | public bool IsNull() 42 | { 43 | return false; 44 | } 45 | 46 | protected Customer() 47 | { 48 | } 49 | 50 | public static Customer NewNull() 51 | { 52 | return new NullCustomer(); 53 | } 54 | } 55 | 56 | public class BillingPlan 57 | { 58 | } 59 | 60 | public class PaymentHistory 61 | { 62 | public int GetWeeksDelinquentInLastYear() 63 | { 64 | return 0; 65 | } 66 | 67 | public static PaymentHistory NewNull() 68 | { 69 | return new NullPaymentHistory(); 70 | } 71 | } 72 | 73 | public class NullCustomer : Customer 74 | { 75 | public bool IsNull() 76 | { 77 | return true; 78 | } 79 | 80 | public string Name 81 | { 82 | get 83 | { 84 | return "occupant"; 85 | } 86 | } 87 | 88 | private BillingPlan _plan; 89 | 90 | public BillingPlan Plan 91 | { 92 | set 93 | { 94 | this._plan = value; 95 | } 96 | } 97 | 98 | public PaymentHistory History 99 | { 100 | get 101 | { 102 | return PaymentHistory.NewNull(); 103 | } 104 | } 105 | } 106 | 107 | public class NullPaymentHistory : PaymentHistory 108 | { 109 | public int GetWeeksDelinquentInLastYear() 110 | { 111 | return 0; 112 | } 113 | } 114 | } 115 | } -------------------------------------------------------------------------------- /RefactorPractice/SimplifyingConditionalExpressions/IntroduceNullObject/README.md: -------------------------------------------------------------------------------- 1 | ## 引入Null Object(Introduce NullObject) 2 | 3 | 需要反覆檢查某個物件是否為null value。 4 | 5 | 將null value替換成null object 6 | 7 | ## 優點: 8 | * 可以不必再關注物件是什麼型別,只要關注呼叫該行為即可。 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/SimplifyingConditionalExpressions/RemoveControlFlag/README.md: -------------------------------------------------------------------------------- 1 | ## 移除控制旗標(Remove Control Flag) 2 | 3 | 在一系列布林運算式中,某個變數帶有控制旗標。 4 | 5 | 以break或return取代旗標 6 | 7 | ## 優點: 8 | * 去除控制旗標會讓條件式真正的用途會清晰許多。 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/SimplifyingConditionalExpressions/RemoveControlFlag/RemoveControlFlag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.SimplifyingConditionalExpressions.RemoveControlFlag 8 | { 9 | internal class RemoveControlFlag 10 | { 11 | public class WithBreak 12 | { 13 | private void CheckSecurity(string[] people) 14 | { 15 | for (int i = 0; i < people.Length; i++) 16 | { 17 | if (people[i].Equals("Don")) 18 | { 19 | SendAlert(); 20 | break; 21 | } 22 | if (people[i].Equals("John")) 23 | { 24 | SendAlert(); 25 | break; 26 | } 27 | } 28 | } 29 | 30 | private void SendAlert() 31 | { 32 | throw new NotImplementedException(); 33 | } 34 | } 35 | 36 | public class WithReturn 37 | { 38 | private void CheckSecurity(string[] people) 39 | { 40 | var found = FoundMiscreant(people); 41 | SomeLaterCode(found); 42 | } 43 | 44 | private string FoundMiscreant(string[] people) 45 | { 46 | for (int i = 0; i < people.Length; i++) 47 | { 48 | if (people[i].Equals("Don")) 49 | { 50 | SendAlert(); 51 | return "Don"; 52 | } 53 | if (people[i].Equals("John")) 54 | { 55 | SendAlert(); 56 | return "John"; 57 | } 58 | } 59 | 60 | return ""; 61 | } 62 | 63 | private void SomeLaterCode(string found) 64 | { 65 | throw new NotImplementedException(); 66 | } 67 | 68 | private void SendAlert() 69 | { 70 | throw new NotImplementedException(); 71 | } 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /RefactorPractice/SimplifyingConditionalExpressions/ReplaceConditionalWithPolymorphism/README.md: -------------------------------------------------------------------------------- 1 | ## 以多型取代條件式(Replace Conditional With Polymorphism) 2 | 3 | 有個條件式,根據物件型別不同而選擇不同的行為。 4 | 5 | 將這個條件式每個分支放進一個一個subclass內的複寫函式中,然後將原始函式宣告成抽象。 6 | 7 | ## 優點: 8 | * 多型可以根據物件的不同型別採取不同的行為,可以不必寫明顯的條件式。 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/SimplifyingConditionalExpressions/ReplaceConditionalWithPolymorphism/ReplaceConditionalWithPolymorphism.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.SimplifyingConditionalExpressions.ReplaceConditionalWithPolymorphism 8 | { 9 | internal class ReplaceConditionalWithPolymorphism 10 | { 11 | public class Employee 12 | { 13 | public const int ENGINERR = 0; 14 | public const int SALESMAN = 1; 15 | public const int MANAGER = 2; 16 | 17 | private EmployeeType _type; 18 | 19 | public int MonthlySalary { get; set; } 20 | 21 | public int Commission { get; set; } 22 | 23 | public int Bonus { get; set; } 24 | 25 | public int PayAmount() 26 | { 27 | return this._type.PayAmount(this); 28 | } 29 | 30 | public int Type 31 | { 32 | get 33 | { 34 | return this._type.GetTypeCode(); 35 | } 36 | } 37 | } 38 | 39 | public abstract class EmployeeType 40 | { 41 | public abstract int GetTypeCode(); 42 | 43 | public abstract int PayAmount(Employee emp); 44 | } 45 | 46 | public class Engineer : EmployeeType 47 | { 48 | public override int GetTypeCode() 49 | { 50 | return Employee.ENGINERR; 51 | } 52 | 53 | public override int PayAmount(Employee emp) 54 | { 55 | return emp.MonthlySalary; 56 | } 57 | } 58 | 59 | public class SalesMan : EmployeeType 60 | { 61 | public override int GetTypeCode() 62 | { 63 | return Employee.SALESMAN; 64 | } 65 | 66 | public override int PayAmount(Employee emp) 67 | { 68 | return emp.MonthlySalary + emp.Commission; 69 | } 70 | } 71 | 72 | public class Manager : EmployeeType 73 | { 74 | public override int GetTypeCode() 75 | { 76 | return Employee.MANAGER; 77 | } 78 | 79 | public override int PayAmount(Employee emp) 80 | { 81 | return emp.MonthlySalary + emp.Bonus; 82 | } 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /RefactorPractice/SimplifyingConditionalExpressions/ReplaceNestedConditionalWithGuardClauses/README.md: -------------------------------------------------------------------------------- 1 | ## 以衛述句取代巢狀條件式(Replace Nested Conditional With Guard Clauses) 2 | 3 | 函式中的條件邏輯使人難以看清正常的執行路徑。 4 | 5 | 使用衛述句(Guard Clauses)表現所有特殊情況 6 | 7 | ## 優點: 8 | * 衛述句代表的意義是`這種情況很罕見,如果有發生請做必要的工作,然後退出`。 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RefactorPractice/SimplifyingConditionalExpressions/ReplaceNestedConditionalWithGuardClauses/ReplaceNestedConditionalWithGuardClauses.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RefactorPractice.SimplifyingConditionalExpressions.ReplaceNestedConditionalWithGuardClauses 8 | { 9 | internal class ReplaceNestedConditionalWithGuardClauses 10 | { 11 | private bool _isDead; 12 | 13 | private bool _isSeparated; 14 | 15 | private bool _isRetired; 16 | 17 | public double GetPayAmount() 18 | { 19 | if (_isDead) 20 | { 21 | return DeadAmount(); 22 | } 23 | if (_isSeparated) 24 | { 25 | return SeparatedAmount(); 26 | } 27 | if (_isRetired) 28 | { 29 | return RetiredAmount(); 30 | } 31 | return NormalPayAmount(); 32 | } 33 | 34 | private double NormalPayAmount() 35 | { 36 | throw new NotImplementedException(); 37 | } 38 | 39 | private double RetiredAmount() 40 | { 41 | throw new NotImplementedException(); 42 | } 43 | 44 | private double SeparatedAmount() 45 | { 46 | throw new NotImplementedException(); 47 | } 48 | 49 | private double DeadAmount() 50 | { 51 | throw new NotImplementedException(); 52 | } 53 | } 54 | } --------------------------------------------------------------------------------