1"use strict";(self.webpackChunkxtrem_tdd=self.webpackChunkxtrem_tdd||[]).push([[424],{2352:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>t,contentTitle:()=>r,default:()=>h,frontMatter:()=>l,metadata:()=>c,toc:()=>a});var s=i(7624),o=i(2172);const l={categories:["Design"],authors:["Yoan Thirion"],problems:["How can I create more explicit methods or functions?","How can I deal with errors in code?"]},r="Avoid Exceptions",c={id:"Flavours/Design/avoid-exceptions",title:"Avoid Exceptions",description:'"Exception handling is the process of responding to the occurrence of exceptions \u2013 anomalous or exceptional conditions requiring special processing \u2013 during the execution of a program." - Wikipedia',source:"@site/content/Flavours/Design/avoid-exceptions.md",sourceDirName:"Flavours/Design",slug:"/Flavours/Design/avoid-exceptions",permalink:"/Flavours/Design/avoid-exceptions",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{categories:["Design"],authors:["Yoan Thirion"],problems:["How can I create more explicit methods or functions?","How can I deal with errors in code?"]},sidebar:"topicSidebar",previous:{title:"Design",permalink:"/category/design"},next:{title:"Command Query Separation",permalink:"/Flavours/Design/command-query-separation"}},t={},a=[{value:"Exceptions vs Errors",id:"exceptions-vs-errors",level:3},{value:"Why?",id:"why",level:2},{value:"Problems",id:"problems",level:2},{value:"How to",id:"how-to",level:2},{value:"Extend the output",id:"extend-the-output",level:3},{value:"Constrain the input",id:"constrain-the-input",level:3},{value:"Hollywood principle",id:"hollywood-principle",level:3},{value:"Constraint",id:"constraint",level:2},{value:"Resources",id:"resources",level:2}];function d(e){const n={a:"a",blockquote:"blockquote",code:"code",h1:"h1",h2:"h2",h3:"h3",img:"img",li:"li",p:"p",pre:"pre",ul:"ul",...(0,o.M)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.h1,{id:"avoid-exceptions",children:"Avoid Exceptions"}),"\n",(0,s.jsxs)(n.blockquote,{children:["\n",(0,s.jsx)(n.p,{children:'"Exception handling is the process of responding to the occurrence of exceptions \u2013 anomalous or exceptional conditions requiring special processing \u2013 during the execution of a program." - Wikipedia'}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"exceptions-vs-errors",children:"Exceptions vs Errors"}),"\n",(0,s.jsx)(n.p,{children:"Exceptions and errors are not the same thing:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["Exceptions should be only used for exceptional situations, which by definition can not be many","\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Fewer exceptions - the better"}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["Exceptions should not leave the sysem boundaries in their original form","\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"It is not user friendly and gives attackers a way to further explore possible system weaknesses"}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["If the thrown exception is processed by our application, we should not use an exception","\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"It is slow and we can deal with it within our boundaries"}),"\n",(0,s.jsxs)(n.li,{children:["Use another mecanism like ",(0,s.jsx)(n.code,{children:"Result"}),", a Monad like ",(0,s.jsx)(n.code,{children:"Either"}),", a callback, ..."]}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["Error/Result explicitly declares the possibility of an error and allows \u201clinear\u201d processing","\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"An implementation on exceptions will be a hidden goto statement"}),"\n",(0,s.jsx)(n.li,{children:"The worse the processing code is from the exception code of an exception, the worse it will be"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"why",children:"Why?"}),"\n",(0,s.jsxs)(n.p,{children:["We should avoid avoid the intensive usage of ",(0,s.jsx)(n.code,{children:"Exceptions"})," for:"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["Making our code the more transparent possible, and so the more readable possible","\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Avoid lies in our method / function definitions"}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.li,{children:"Keeping them for exceptional situations"}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"problems",children:"Problems"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"How can I create more explicit methods or functions?"}),"\n",(0,s.jsx)(n.li,{children:"How can I deal with errors in code?"}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"how-to",children:"How to"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"We have different options to avoid exceptions"}),"\n",(0,s.jsxs)(n.li,{children:["Let's take this example and present a few of them:","\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["Imagine we have a simple ",(0,s.jsx)(n.code,{children:"Divide"})," method in our code"]}
1),"\n",(0,s.jsxs)(n.li,{children:["This method contains a lie in terms of contract / public API","\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["It says that for 2 given ",(0,s.jsx)(n.code,{children:"double"})," the method returns a ",(0,s.jsx)(n.code,{children:"double"}),": ",(0,s.jsx)(n.code,{children:"double -> double -> double"})]}),"\n",(0,s.jsx)(n.li,{children:"What happens if we pass 0 as a denominator?"}),"\n",(0,s.jsxs)(n.li,{children:["The defintion contains a ",(0,s.jsx)(n.code,{children:"lie"})," (maybe by omission)"]}),"\n"]}),"\n"]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-csharp",children:"public static double Divide(double numerator, double denominator)\n => numerator / denominator;\n"})}),"\n",(0,s.jsx)(n.h3,{id:"extend-the-output",children:"Extend the output"}),"\n",(0,s.jsxs)(n.p,{children:["We can do it by using a ",(0,s.jsx)(n.code,{children:"Result"})," type:"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Here is a simple one created for this example"}),"\n"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-csharp",children:'public static Result<double, string> Divide(double numerator, double denominator)\n => denominator == 0\n ? Failure("Invalid denominator")\n : Success(numerator / denominator);\n'})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Its basic implementation"}),"\n"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-csharp",children:"public record Result<TSuccess, TFailure>\n where TFailure : class\n{\n private readonly TSuccess? _success;\n private readonly TFailure? _failure;\n\n private Result(TSuccess success) => _success = success;\n private Result(TFailure failure) => _failure = failure;\n\n public static Result<TSuccess, TFailure> Success(TSuccess success) => new(success);\n public static Result<TSuccess, TFailure> Failure(TFailure failure) => new(failure);\n\n public void Match(Action<TSuccess> onSuccess, Action<TFailure> onFailure)\n {\n if (IsFailure())\n onFailure(_failure!);\n else onSuccess(_success!);\n }\n\n private bool IsFailure() => _failure is { };\n\n public TSuccess IfFailure(TSuccess ifFail)\n => IsFailure()\n ? ifFail\n : _success!;\n}\n"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["As a consumer / caller of this method we now have to deal with the return type:","\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"We have to be exhaustive in the treatment of the result"}),"\n",(0,s.jsxs)(n.li,{children:["Our method does not contain lie anymore and express explicitly that the operation can fail: ",(0,s.jsx)(n.code,{children:"double -> double -> Result"})]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-csharp",children:'result.Match(success => Console.WriteLine($"Success {success}"),\n failure => Console.WriteLine($"Failure: {failure}"));\n'})}),"\n",(0,s.jsxs)(n.blockquote,{children:["\n",(0,s.jsxs)(n.p,{children:["Alternatively you may use monads like ",(0,s.jsx)(n.code,{children:"Either"}),", ",(0,s.jsx)(n.code,{children:"Try"}),", ",(0,s.jsx)(n.code,{children:"Option"}),", ",(0,s.jsx)(n.code,{children:"Maybe"}),", ..."]}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"constrain-the-input",children:"Constrain the input"}),"\n",(0,s.jsx)(n.p,{children:"It has huge advantages of constraining inputs / arguments:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"You don't need to write preventive code anymore (No more guard clause everywhere)"}),"\n",(0,s.jsx)(n.li,{children:"We make it impossible to represent invalid state"}),"\n",(0,s.jsx)(n.li,{children:"We have a more business related concepts that are expressed in our code"}),"\n"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-csharp",children:"public static double Divide(double numerator, NonZeroDouble denominator)\n => numerator / denominator.ToDouble();\n"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["Here we express the fact that for instantiating a ",(0,s.jsx)(n.code,{children:"NonZeroDouble"})," we need to pass a valid ",(0,s.jsx)(n.code,{children:"double"})," different from 0"]}),"\n"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-csharp",children:'public re
1cord NonZeroDouble\n{\n private readonly double _value;\n\n private NonZeroDouble(double value) => _value = value;\n\n public static NonZeroDouble From(double value)\n => value == 0\n ? throw new ArgumentException("0 is not allowed for NonZeroDouble")\n : new NonZeroDouble(value);\n\n public double ToDouble() => _value;\n}\n\npublic static class DoubleExtensions\n{\n public static NonZeroDouble ToNonZeroDouble(this double value) => NonZeroDouble.From(value);\n}\n'})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsx)(n.p,{children:"We centralize its instantiation logic of this kind of data structure through"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["A ",(0,s.jsx)(n.code,{children:"Factory Method"})]}),"\n",(0,s.jsxs)(n.li,{children:["A ",(0,s.jsx)(n.code,{children:"private constructor"})]}),"\n",(0,s.jsxs)(n.li,{children:["An ",(0,s.jsx)(n.code,{children:"extension method"})," to convert from ",(0,s.jsx)(n.code,{children:"double"})]}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:["We could return a default value instead of throwing an ",(0,s.jsx)(n.code,{children:"Exception"})," as well"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"It is a business decision at the end"}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsx)(n.p,{children:"As a consumer / caller of this method we now have to deal with the new input type"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["Our method expresses explicitly what is accepted as input: ",(0,s.jsx)(n.code,{children:"double -> NonZeroInteger -> double"})]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-csharp",children:"var result = Divide(9, 3d.ToNonZeroDouble());\n"})}),"\n",(0,s.jsx)(n.h3,{id:"hollywood-principle",children:"Hollywood principle"}),"\n",(0,s.jsxs)(n.blockquote,{children:["\n",(0,s.jsx)(n.p,{children:"Don't Call Us, We'll Call You"}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.img,{alt:"Hollywood principle",src:i(5696).c+"",width:"409",height:"606"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:["One of the most popular way to implement this principle is to use ",(0,s.jsx)(n.code,{children:"events"})," or ",(0,s.jsx)(n.code,{children:"callbacks"})]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:["The basic idea behind it is ",(0,s.jsx)(n.code,{children:"let the caller decide what happens next"})]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:["Let's use callbacks / continuation functions in our ",(0,s.jsx)(n.code,{children:"Divide"})," method"]}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-csharp",children:'public static void Divide(\n double numerator,\n double denominator,\n Action<double> onSuccess,\n Action<string> onError)\n{\n if (denominator == 0) onError("Invalid denominator");\n else onSuccess(numerator / denominator);\n}\n'})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["As a consumer / caller of this method we now have to pass callback methods for success and failure","\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["The method returns ",(0,s.jsx)(n.code,{children:"void"})," now: ",(0,s.jsx)(n.code,{children:"double -> double -> (double -> void) -> (string -> void)"})," -> void"]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-csharp",children:'Divide(9,\n 0,\n success => Console.WriteLine($"Success {success}"),\n failure => Console.WriteLine($"Failure : {failure}"));\n'})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"As a side effect, continuation has complexified our method signature"}),"\n",(0,s.jsx)(n.li,{children:"Use this principle with care to avoid callback hell"}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.img,{alt:"Callback hell",src:i(112).c+"",width:"721",height:"420"})}),"\n",(0,s.jsxs)(n.blockquote,{children:["\n",(0,s.jsx)(n.p,{children:"Other alternatives exist and can depend on your programming language / paradigm"}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"constraint",children:"Constraint"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["If you are already using ",(0,s.jsx)(n.code,{children:"Exceptions"})," to handle ",(0,s.jsx)(n.code,{children:"errors"})," remove them using one of the alternative presented or another"]}),"\n",(0,s.jsx)(n.li,{children:"If not, think about a first edge that you do not support yet and use one of the alternative presented to manage it"}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"resources",children:"Resources"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"https://youtu.be/a1ye9eGTB98",children:"Don't throw exceptions in C#. Do this instead"})," by Nick Chapsas"]}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"https://wiki.c2.com/?DontThrowGenericExceptions",children:"Don't throw Generic Exceptions"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"https://sudonull.com/post/7531-How-to-work-with-exceptions-in-DDD",children:"How to work with exceptions in DDD"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"https://wiki.c2.com/?HollywoodPrinciple",children:"Hollywood Principle"})}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,o.M)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(d,{...e})}):d(e)}},112:(e,n,i)=>{i.d(n,{c:()=>s});const s=i.p+"assets/images/callback-hell-8e2a9461a027d705721ecb5a4932957b.webp"},5696:(e,n,i)=>{i.d(n,{c:()=>s});const s=i.p+"assets/images/hollywood-principle-bd15d1389c8005f08cb95eaa8896669d.webp"},2172:(e,n,i)=>{i.d(n,{I:()=>c,M:()=>r});var s=i(1504);const o={},l=s.createContext(o);function r(e){const n=s.useContext(l);return s.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:r(e.components),s.createElement(l.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.