Pricing an order
Trace pricing priority, absence and the selected result; a short conditional is not the whole policy. See readability under real business rules for policy branches, initialization and coordination.
A checkout service charges Standard customers 3%, Preferred customers 1% and Staff customers nothing. A promotion halves that fee when the order is at least 100 currency units. The final fee is rounded to two decimal places using midpoint-to-even rounding.
Every implementation rejects a negative amount and an unknown tier. Zero is a valid amount. The promotion boundary is inclusive. There is no tax, exchange-rate conversion or database access in this calculation.
The action and methods below belong inside the application's pricing class or module. The Python function is module-level. The tier is an integer supplied by a boundary layer: 0 is Standard, 1 is Preferred and 2 is Staff. Explicit validation is necessary because a caller can supply an unsupported integer in every version.
DirectSharp
// Pricing policy: preserve the order of guards and selected calculations.
action CalculateFee(amount as Decimal, tier as Integer, promotion as Boolean)
returns Decimal
require amount >= 0 otherwise new Global.System.ArgumentOutOfRangeException("amount")
let rate as Decimal = select tier
case 0
take 0.03
case 1
take 0.01
case 2
take 0
else
throw new Global.System.ArgumentOutOfRangeException("tier")
end select
let fee as Decimal = amount * rate
if promotion and amount >= 100 then
fee = fee / 2
end if
return Global.System.Decimal.Round(
fee, 2, Global.System.MidpointRounding.ToEven)
end action
require makes the entry condition explicit. otherwise supplies the failure expression. Expression-valued select supplies the rate through take; return exits the action. The exceptional case does not need to produce a rate because it cannot complete normally. Decimal context gives the rate literals their intended type.
C#
// Pricing policy: preserve the order of guards and selected calculations.
using System;
public static decimal CalculateFee(decimal amount, int tier, bool promotion)
{
if (amount < 0)
throw new ArgumentOutOfRangeException(nameof(amount));
decimal rate = tier switch
{
0 => 0.03m,
1 => 0.01m,
2 => 0m,
_ => throw new ArgumentOutOfRangeException(nameof(tier))
};
decimal fee = amount * rate;
if (promotion && amount >= 100m)
fee /= 2m;
return decimal.Round(fee, 2, MidpointRounding.ToEven);
}
The switch expression performs the same selection. The m suffix fixes a literal's decimal type. C# expresses the action result in the method header and uses braces for its body. The explicit argument guard carries the same application rule as DirectSharp's contract.
VB.NET
' Pricing policy: preserve the order of guards and selected calculations.
Option Strict On
Option Explicit On
Option Infer On
Imports System
Public Function CalculateFee(amount As Decimal, tier As Integer,
promotion As Boolean) As Decimal
If amount < 0D Then
Throw New ArgumentOutOfRangeException(NameOf(amount))
End If
Dim rate As Decimal
Select Case tier
Case 0
rate = 0.03D
Case 1
rate = 0.01D
Case 2
rate = 0D
Case Else
Throw New ArgumentOutOfRangeException(NameOf(tier))
End Select
Dim fee As Decimal = amount * rate
If promotion AndAlso amount >= 100D Then
fee /= 2D
End If
Return Decimal.Round(fee, 2, MidpointRounding.ToEven)
End Function
Select Case assigns the same local rather than producing an expression result. AndAlso is the short-circuit Boolean operator. Using And instead can change evaluation behavior when the right side contains a call, even though this particular numeric comparison has no effect.
Python
# Pricing policy: preserve the order of guards and selected calculations.
from decimal import Decimal, ROUND_HALF_EVEN
def calculate_fee(amount: Decimal, tier: int, promotion: bool) -> Decimal:
if amount < 0:
raise ValueError("amount must be nonnegative")
match tier:
case 0:
rate = Decimal("0.03")
case 1:
rate = Decimal("0.01")
case 2:
rate = Decimal("0")
case _:
raise ValueError("unknown tier")
fee = amount * rate
if promotion and amount >= Decimal("100"):
fee /= Decimal("2")
return fee.quantize(Decimal("0.01"), rounding=ROUND_HALF_EVEN)
match selects the same rate, and Decimal is constructed from text to avoid introducing binary floating-point error. Invalid values produce Python's ValueError; the .NET versions use ArgumentOutOfRangeException. These are the corresponding argument-failure categories, rather than identical exception types across runtimes.
The Python contract assumes a Decimal amount, an integer tier and a Boolean promotion flag. Those annotations are not runtime guards. Python's bool is also a subtype of int, so a caller passing True as the tier would match 1. A dynamically exposed API should validate its input types before calling this calculation.
Boundary cases
| Input | Fee or failure |
|---|---|
| Standard, 100, no promotion | 3.00 |
| Preferred, 100, promotion | 0.50 |
| Standard, 99.99, promotion | 3.00; promotion does not apply |
| Staff, 500, either promotion flag | 0.00 |
| Standard, 0.50, no promotion | 0.02; 0.015 rounds to the even cent |
| Any supported tier, 0 | 0.00 |
| Negative amount | Argument failure |
| Tier 3 | Argument failure |
These are financial examples with modest finite decimal values. Python's Decimal context and .NET Decimal have different precision, range and overflow behavior. Equivalent production boundaries must account for those differences, rather than assuming both numeric systems accept the same extreme inputs.
What changes for the reader
All four versions make the rules visible. DirectSharp's contract and result-producing select separate a precondition from a choice. C#'s switch expression is compact. VB.NET's named blocks and assignments are familiar to many .NET business developers. Python removes most type syntax, with more responsibility at dynamic input boundaries.
A typed application could replace the integer tier with an enum and still validate values arriving from serialization or imported CLR code. The integer representation here keeps that boundary explicit and avoids comparing a validated enum in one language with unrestricted input in another.
For several orders, an ordinary loop can call this calculation sequentially. DirectSharp uses for_each order in orders ... end for_each; C# uses foreach, VB.NET uses For Each ... Next, and Python uses for. Moving the calculation into a concurrent loop changes scheduling and error policy, even when every order's successful fee remains the same.
Next: constructing and validating a customer · All comparisons