316 lines
No EOL
16 KiB
XML
316 lines
No EOL
16 KiB
XML
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>alguidelines.dev - Business Central Design Patterns – Design</title><link>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/</link><description>Recent content in Design on alguidelines.dev - Business Central Design Patterns</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><atom:link href="https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/index.xml" rel="self" type="application/rss+xml"/><item><title>Docs: By Reference Parameters</title><link>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/by-reference-parameters/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/by-reference-parameters/</guid><description>
|
||
<p>Do not declare parameters by reference if their values are not intended to be changed.</p>
|
||
<p>Unintentional value changes might propagate. Also, it might lead people to believe that value changes are intended.</p>
|
||
<p>Bad code</p>
|
||
<pre><code>LOCAL PROCEDURE ShowMessage@15(VAR Text@1000 : Text[250]);
|
||
BEGIN
|
||
Text := GetMessageText;
|
||
IF (Text &lt;&gt; '') AND GenJnlLineInserted THEN
|
||
MESSAGE(Text);
|
||
END;
|
||
</code></pre>
|
||
<p>Good code</p>
|
||
<pre><code>LOCAL PROCEDURE ShowMessage@15(Text@1000 : Text[250]);
|
||
BEGIN
|
||
Text := GetMessageText;
|
||
IF (Text &lt;&gt; '') AND GenJnlLineInserted THEN
|
||
MESSAGE(Text);
|
||
END;
|
||
</code></pre></description></item><item><title>Docs: Class Coupling</title><link>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/class-coupling/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/class-coupling/</guid><description>
|
||
<p>Do not write functions that have high class coupling. This makes the code hard to maintain.</p>
|
||
<p>Bad code</p>
|
||
<pre><code>Any procedure / trigger that has class coupling of &gt; 30
|
||
</code></pre>
|
||
<p>Good code</p>
|
||
<pre><code>Any procedure / trigger that has class coupling of &lt;= 30\.
|
||
Class coupling is computed by summing the unique instances of the following in a code block:
|
||
- every unique usage of a complex C/AL data type (table, codeunit, etc) as 1\.
|
||
- every unique usage of a DotNet type as 1\.
|
||
</code></pre></description></item><item><title>Docs: Cyclomatic Complexity</title><link>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/cyclomatic-complexity/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/cyclomatic-complexity/</guid><description>
|
||
<p>Do not write functions that have high cyclomatic complexity. This makes the code hard to maintain.</p>
|
||
<p>Bad code</p>
|
||
<pre><code>Any procedure / trigger that has a cyclomatic complexity &gt; 25, using the CC3 version mentioned in [this article][anchor0].
|
||
</code></pre>
|
||
<p>Good code</p>
|
||
<pre><code>Any procedure / trigger that has a cyclomatic complexity &lt;= 25, using the CC3 version.
|
||
The CC3 version is computed by summing the following in a code block:
|
||
- each IF statement as 1\.
|
||
- each entire CASE as 1\.
|
||
</code></pre></description></item><item><title>Docs: Encapsulate Local Functionality</title><link>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/encapsulate-local-functionality/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/encapsulate-local-functionality/</guid><description>
|
||
<p>Any function used local must be defined as local.</p></description></item><item><title>Docs: FINDSET FINDFIRST FINDLAST</title><link>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/findset-findfirst-findlast/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/findset-findfirst-findlast/</guid><description>
|
||
<p>FINDSET, FIND('+') or FIND('-') should only be used when NEXT is used and vice versa.</p>
|
||
<p>Bad code</p>
|
||
<pre><code>IF Cust.FIND('-') THEN
|
||
ERROR(CustIsBlockErr)
|
||
</code></pre>
|
||
<p>Good code</p>
|
||
<pre><code>IF Cust.FINDFIRST THEN
|
||
ERROR(CustIsBlockErr)
|
||
</code></pre>
|
||
<p>Bad code</p>
|
||
<pre><code>IF Cust.FINDFIRST THEN
|
||
REPEAT
|
||
...
|
||
UNTIL Cust.NEXT = 0;
|
||
</code></pre>
|
||
<p>Good code</p>
|
||
<pre><code>IF Cust.FINDSET THEN
|
||
REPEAT
|
||
...
|
||
UNTIL Cust.NEXT = 0;
|
||
</code></pre></description></item><item><title>Docs: Initialized Variables</title><link>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/initialized-variables/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/initialized-variables/</guid><description>
|
||
<p>Variables should always be set to a specific value, before they are used.</p>
|
||
<p>Bad code</p>
|
||
<pre><code>PROCEDURE SetPurchLine@22(VAR CurrentPurchLine@1000 : Record 39);
|
||
VAR
|
||
Pegging@1001 : Boolean;
|
||
BEGIN
|
||
IF Pegging THEN
|
||
CurrQuantity := CurrentPurchLine.&quot;Quantity (Base)&quot;
|
||
ELSE
|
||
CurrQuantity := CurrentPurchLine.&quot;Outstanding Qty. (Base)&quot;;
|
||
END;
|
||
</code></pre>
|
||
<p>Good code</p>
|
||
<pre><code>PROCEDURE SetPurchLine@22(VAR CurrentPurchLine@1000 : Record 39);
|
||
VAR
|
||
Pegging@1001 : Boolean;
|
||
BEGIN
|
||
Pegging := IsPegging(CurrentPurchLine);
|
||
IF Pegging THEN
|
||
CurrQuantity := CurrentPurchLine.&quot;Quantity (Base)&quot;
|
||
ELSE
|
||
CurrQuantity := CurrentPurchLine.&quot;Outstanding Qty. (Base)&quot;;
|
||
END;
|
||
</code></pre>
|
||
<p>Bad code</p>
|
||
<pre><code>// In the example below, the function will always return FALSE.
|
||
PROCEDURE GetItemsToPlan@22() : Boolean;
|
||
BEGIN
|
||
SETRANGE(&quot;Document Type&quot;,&quot;Document Type&quot;::Order);
|
||
...
|
||
FINDSET
|
||
END;
|
||
</code></pre>
|
||
<p>Good code</p>
|
||
<pre><code>PROCEDURE GetItemsToPlan@22() : Boolean;
|
||
BEGIN
|
||
SETRANGE(&quot;Document Type&quot;,&quot;Document Type&quot;::Order);
|
||
...
|
||
EXIT(FINDSET)
|
||
END;
|
||
</code></pre></description></item><item><title>Docs: Maintainability Index</title><link>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/maintainability-index/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/maintainability-index/</guid><description>
|
||
<p><a href="http://blogs.msdn.com/b/codeanalysis/archive/2007/11/20/maintainability-index-range-and-meaning.aspx">Maintainability Index</a>: Do not write functions that have a very low maintainability index. This makes the code hard to maintain.</p>
|
||
<p>Bad code</p>
|
||
<pre><code>Any procedure / trigger that has a maintainability index &lt; 20
|
||
</code></pre>
|
||
<p>Good code</p>
|
||
<pre><code>Any procedure / trigger that has a maintainability index &gt;= 20\.
|
||
The Maintainability Index is computed as a function:
|
||
- Lines Of Code (inverse proportional)
|
||
- the Halstead Volume
|
||
- Cyclomatic Complexity (inverse proportional).
|
||
</code></pre>
|
||
<p>More info</p>
|
||
<ul>
|
||
<li><a href="http://en.wikipedia.org/wiki/Halstead_complexity_measures">Halstead Volume</a></li>
|
||
<li><a href="http://www.aivosto.com/project/help/pm-complexity.html">Cyclomatic Complexity</a></li>
|
||
</ul>
|
||
<p>Bad code</p>
|
||
<pre><code>Any procedure / trigger that is &gt; 100 lines of code
|
||
</code></pre>
|
||
<p>Good code</p>
|
||
<pre><code>Any procedure / trigger that is &lt;= 100 lines of code.
|
||
A full C/AL Statement counts as 1 line of code
|
||
</code></pre></description></item><item><title>Docs: Parameter Placeholders</title><link>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/parameter-placeholders/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/parameter-placeholders/</guid><description>
|
||
<p>The number of parameters passed to a string must match the placeholders.</p>
|
||
<p>Bad code</p>
|
||
<pre><code>CannotDeleteLineErr@1005 : TextConst 'ENU=You cannot delete this line because one or more rating values exists.';
|
||
...
|
||
ERROR(CannotDeleteLineErr,TABLECAPTION);
|
||
</code></pre>
|
||
<p>Good code</p>
|
||
<pre><code>CannotDeleteLineErr@1005 : TextConst 'ENU=You cannot delete this line because one or more rating values exists.';
|
||
...
|
||
ERROR(CannotDeleteLineErr);
|
||
</code></pre>
|
||
<p>Bad code</p>
|
||
<pre><code>CannotUseThisFieldErr@1020 : TextConst 'ENU=You cannot use this field for %2 fields.';
|
||
...
|
||
ERROR(CannotUseThisFieldErr,0,Field.Class);
|
||
</code></pre>
|
||
<p>Good code</p>
|
||
<pre><code>CannotUseThisFieldErr@1020 : TextConst 'ENU=You cannot use this field for %1 fields.';
|
||
...
|
||
ERROR(CannotUseThisFieldErr,Field.Class);
|
||
</code></pre>
|
||
<h3 id="heading"></h3></description></item><item><title>Docs: Static Object Invocation</title><link>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/static-object-invocation/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/static-object-invocation/</guid><description>
|
||
<p>Call objects statically whenever possible. It reduces extra noise and removes extra variables. Downside: changing the name of the object which is called statically will need a code update.</p>
|
||
<p>Bad code</p>
|
||
<pre><code>LOCAL PROCEDURE Code@1();
|
||
VAR
|
||
CAJnlPostBatch@1001 : Codeunit 1103;
|
||
BEGIN
|
||
CAJnlPostBatch.Run(CostJnlLine);
|
||
END;
|
||
</code></pre>
|
||
<p>Good code</p>
|
||
<pre><code>LOCAL PROCEDURE Code@1();
|
||
BEGIN
|
||
CODEUNIT.RUN(CODEUNIT::&quot;CA Jnl.-Post Batch&quot;,CostJnlLine);
|
||
END;
|
||
</code></pre></description></item><item><title>Docs: Unreachable Code</title><link>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/unreachable-code/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/unreachable-code/</guid><description>
|
||
<p>Do not write code that will never be hit.</p>
|
||
<p>It affects code readability and can lead to wrong assumptions.</p>
|
||
<p>Bad code</p>
|
||
<pre><code>IF Type &lt;&gt; Type::FIELD THEN BEGIN
|
||
...
|
||
ERROR(...);
|
||
RecRef.CLOSE;
|
||
END;
|
||
</code></pre>
|
||
<p>Good code</p>
|
||
<pre><code>IF Type &lt;&gt; Type::FIELD THEN BEGIN
|
||
...
|
||
RecRef.CLOSE;
|
||
ERROR(...);
|
||
END;
|
||
</code></pre></description></item><item><title>Docs: Unused Initialized Variables</title><link>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/unused-initialized-variables/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/unused-initialized-variables/</guid><description>
|
||
<p>The value assigned to a variable must be used. Else the variable is not necessary.</p>
|
||
<p>Bad code</p>
|
||
<pre><code>PROCEDURE AddEntities@1(FilterStr@1000 : Text[250]);
|
||
VAR
|
||
Vendor@1001 : Record 23;
|
||
Count@1002 : Integer;
|
||
BEGIN
|
||
Count := 0;
|
||
Vendor.SETFILTER(&quot;No.&quot;,FilterStr);
|
||
IF Vendor.FINDSET THEN
|
||
REPEAT
|
||
&quot;User ID&quot; := USERID;
|
||
&quot;Vendor No.&quot; := Vendor.&quot;No.&quot;;
|
||
IF INSERT THEN
|
||
Count += 1;
|
||
UNTIL Vendor.NEXT = 0;
|
||
END;
|
||
</code></pre>
|
||
<p>Good code</p>
|
||
<pre><code>PROCEDURE AddEntities@1(FilterStr@1000 : Text[250]);
|
||
VAR
|
||
Vendor@1001 : Record 23;
|
||
BEGIN
|
||
Vendor.SETFILTER(&quot;No.&quot;,FilterStr);
|
||
IF Vendor.FINDSET THEN
|
||
REPEAT
|
||
&quot;User ID&quot; := USERID;
|
||
&quot;Vendor No.&quot; := Vendor.&quot;No.&quot;;
|
||
IF INSERT THEN;
|
||
UNTIL Vendor.NEXT = 0;
|
||
END;
|
||
</code></pre></description></item><item><title>Docs: Unused Variables</title><link>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/unused-variables/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/unused-variables/</guid><description>
|
||
<p>Do not declare variables that are unused.</p>
|
||
<p>Unused variables affect readability.</p>
|
||
<p>Bad code</p>
|
||
<pre><code>PROCEDURE CheckPostingDate@23(CaptionEntryNo@1005 : Text[50]);
|
||
BEGIN
|
||
IF GenJnlCheckLine.DateNotAllowed(PostingDate) THEN
|
||
ERROR(DateNotAllowedErr,Caption,EntryNo)
|
||
IF PostingDate &gt; MaxPostingDate THEN
|
||
MaxPostingDate := PostingDate;
|
||
END
|
||
</code></pre>
|
||
<p>Good code</p>
|
||
<pre><code>PROCEDURE CheckPostingDate@23();
|
||
BEGIN
|
||
IF GenJnlCheckLine.DateNotAllowed(PostingDate) THEN
|
||
ERROR(DateNotAllowedErr,Caption,EntryNo);
|
||
IF PostingDate &gt; MaxPostingDate THEN
|
||
MaxPostingDate := PostingDate;
|
||
END;
|
||
</code></pre>
|
||
<p>Bad code</p>
|
||
<pre><code>PROCEDURE IsReturned@14(EntryNo@1002 : Integer) : Decimal;
|
||
VAR
|
||
ItemEntry@1000 : Record 32;
|
||
Quantity@1003 : Integer;
|
||
BEGIN
|
||
EXIT(-OutboundApplied(EntryNo,TRUE) - InboundApplied(EntryNo,TRUE));
|
||
END;
|
||
</code></pre>
|
||
<p>Good code</p>
|
||
<pre><code>PROCEDURE IsReturned@14(EntryNo@1002 : Integer) : Decimal;
|
||
BEGIN
|
||
EXIT(-OutboundApplied(EntryNo,TRUE) - InboundApplied(EntryNo,TRUE));
|
||
END;
|
||
</code></pre></description></item><item><title>Docs: Variable Capacity Mismatch</title><link>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/variable-capacity-mismatch/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/variable-capacity-mismatch/</guid><description>
|
||
<p>Do not assign a value to a variable whose capacity is smaller.</p>
|
||
<p>It will throw an error at runtime.</p>
|
||
<p>Bad code</p>
|
||
<pre><code>FileName@1010 : Text[250];
|
||
...
|
||
UploadedFileName@1016 : Text[1024];
|
||
...
|
||
FileName := UploadedFileName;
|
||
</code></pre>
|
||
<p>Good code</p>
|
||
<pre><code>FileName@1010 : Text[1024];
|
||
...
|
||
UploadedFileName@1016 : Text[1024];
|
||
...
|
||
FileName := UploadedFileName;
|
||
</code></pre>
|
||
<p>Bad code</p>
|
||
<pre><code>FileName@1010 : Text[250];
|
||
...
|
||
UploadedFileName@1016 : Text[1024];
|
||
...
|
||
FileName := UploadedFileName;
|
||
</code></pre>
|
||
<p>Good code</p>
|
||
<pre><code>FileName@1010 : Text[250];
|
||
...
|
||
UploadedFileName@1016 : Text[1024];
|
||
...
|
||
FileName := COPYSTR(UploadedFileName,1,250); // In case only the first 250 chars are needed. Similar for fields
|
||
</code></pre>
|
||
<p>Bad code</p>
|
||
<pre><code>VAR
|
||
ExceededNumberTxt@001 : 'ENU=Warning: Exceeded number of unsent documents/requests'
|
||
Subject@1002 : Text[50];
|
||
...
|
||
BEGIN
|
||
...
|
||
Subject := ExceededNumberTxt;
|
||
</code></pre>
|
||
<p>Good code</p>
|
||
<pre><code>VAR
|
||
ExceededNumberTxt@001 : 'ENU=Warning: Exceeded number of unsent documents/requests'
|
||
Subject@1002 : Text[100];
|
||
...
|
||
BEGIN
|
||
...
|
||
Subject := ExceededNumberTxt';
|
||
</code></pre></description></item><item><title>Docs: WITH Scope Name Collision</title><link>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/with-scope-name-collision/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alguidelines.dev/docs/navpatterns/3-cal-coding-guidelines/design/with-scope-name-collision/</guid><description>
|
||
<p>Do not use the WITH scope when it has a variable whose name is the same as a local variable. This can lead to wrong code assumptions.</p>
|
||
<p><strong>Given that</strong> <br>
|
||
&ldquo;Contract Type&rdquo; is a field on table ServiceContractHeader, then in the following example there is a parameter name clash with the field name. Which one will be used?</p>
|
||
<p>Bad code</p>
|
||
<pre><code>PROCEDURE InsertData@1(&quot;Contract Type&quot;@1000 : Option...);
|
||
...
|
||
BEGIN
|
||
...
|
||
WITH ServiceContractHeader DO BEGIN
|
||
...
|
||
DimMgt.InsertServContractDim(...,&quot;Contract Type&quot;,&quot;Contract No.&quot;,0,...);
|
||
END;
|
||
</code></pre>
|
||
<p>Good code</p>
|
||
<pre><code>PROCEDURE InsertData@1(ContractType@1000 : Option...);
|
||
...
|
||
BEGIN
|
||
...
|
||
WITH ServiceContractHeader DO BEGIN
|
||
...
|
||
DimMgt.InsertServContractDim(...,ContractType,&quot;Contract No.&quot;,0,...);
|
||
END;
|
||
</code></pre></description></item></channel></rss> |