Skip to content
Browse files

Introduce value objects project

  • Loading branch information...
1 parent 7934edf commit 9b68543271de2d86027ef7dfdcd28a75181b903d @abdullin abdullin committed Dec 2, 2012
View
424 E016-value-objects/sample-csharp/CurrencyAmount.cs
@@ -0,0 +1,424 @@
+using System;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+using System.Runtime.Serialization;
+
+namespace E016_value_objects
+{
+ public enum CurrencyType
+ {
+ None,
+ Eur,
+ Usd,
+ Aud,
+ Cad,
+ Chf,
+ Gbp,
+ Jpy,
+ Rub,
+ Mxn,
+ }
+
+ /// <summary>
+ /// Decimal that also has currency type assotiated. It is used
+ /// for enforcing logical consistency within the billing.
+ ///
+ /// We want to reduce chances of messing up by mixing different
+ /// currencies together.
+ /// </summary>
+ [DataContract(Namespace = "Lokad.Core")]
+ public struct CurrencyAmount : IEquatable<CurrencyAmount>, IFormattable
+ {
+ /// <summary>
+ /// Default empty currency amount with undefined currency type
+ /// </summary>
+ public static readonly CurrencyAmount Zero = default(CurrencyAmount);
+
+ /// <summary>Currency type</summary>
+ [DataMember(Order = 1)]
+ public readonly CurrencyType Currency;
+
+ /// <summary>
+ /// Value in the given currency
+ /// </summary>
+ [DataMember(Order = 2)]
+ public readonly decimal Value;
+
+ /// <summary>
+ /// Initializes a new instance of the
+ /// <see cref="CurrencyAmount" />
+ /// struct.
+ /// </summary>
+ /// <param name="currency">The currency.</param>
+ /// <param name="value">The value.</param>
+ public CurrencyAmount(CurrencyType currency, decimal value)
+ {
+ Currency = currency;
+ Value = value;
+ }
+
+ public static CurrencyAmount Parse(string source)
+ {
+ // required for JSON conversion back
+ var items = source.Split(' ');
+ var type = CurrencyType.None;
+ var @decimal = decimal.Parse(items[0], CultureInfo.InvariantCulture);
+
+ if (items.Length > 1)
+ {
+ type = (CurrencyType)Enum.Parse(typeof(CurrencyType), items[1], true);
+ }
+ return @decimal.In(type);
+ }
+
+
+ /// <summary>
+ /// Indicates whether the current object is equal to another object of the same type.
+ /// </summary>
+ /// <param name="other">
+ /// An object to compare with this object.
+ /// </param>
+ /// <returns>
+ /// true if the current object is equal to the
+ /// <paramref name="other" />
+ /// parameter; otherwise, false.
+ /// </returns>
+ public bool Equals(CurrencyAmount other)
+ {
+ return Equals(other.Currency, Currency) && other.Value == Value;
+ }
+
+ /// <summary>
+ /// Determines whether the specified
+ /// <see cref="System.Object" />
+ /// is equal to this instance.
+ /// </summary>
+ /// <param name="obj">
+ /// The
+ /// <see cref="System.Object" />
+ /// to compare with this instance.
+ /// </param>
+ /// <returns>
+ /// <c>true</c>
+ /// if the specified
+ /// <see cref="System.Object" />
+ /// is equal to this instance; otherwise,
+ /// <c>false</c>
+ /// .
+ /// </returns>
+ /// <exception cref="T:System.NullReferenceException">
+ /// The
+ /// <paramref name="obj" />
+ /// parameter is null.
+ /// </exception>
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (obj.GetType() != typeof(CurrencyAmount)) return false;
+ return Equals((CurrencyAmount)obj);
+ }
+
+ /// <summary>
+ /// Returns a hash code for this instance.
+ /// </summary>
+ /// <returns>
+ /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
+ /// </returns>
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ return (Currency.GetHashCode() * 397) ^ Value.GetHashCode();
+ }
+ }
+
+ /// <summary>
+ /// Returns a
+ /// <see cref="System.String" />
+ /// that represents this instance, using the
+ /// provided format options to pass to the
+ /// <see cref="decimal.ToString(string,System.IFormatProvider)" />
+ /// </summary>
+ /// <param name="format">The format.</param>
+ /// <param name="formatProvider">The format provider.</param>
+ /// <returns>
+ /// A
+ /// <see cref="System.String" />
+ /// that represents this instance.
+ /// </returns>
+ [Pure]
+ public string ToString(string format, IFormatProvider formatProvider)
+ {
+ var amount = Value.ToString(format, formatProvider);
+
+ if (Currency == CurrencyType.None)
+ return amount;
+
+ return string.Format("{0} {1}", amount, Currency.ToString().ToUpperInvariant());
+ }
+ [Pure]
+ public string ToString(string format)
+ {
+ return ToString(format, CultureInfo.InvariantCulture);
+ }
+
+ /// <summary>
+ /// Returns a
+ /// <see cref="System.String" />
+ /// that represents this instance.
+ /// </summary>
+ /// <returns>
+ /// A
+ /// <see cref="System.String" />
+ /// that represents this instance.
+ /// </returns>
+ [Pure]
+ public override string ToString()
+ {
+ var amount = Value.ToString(CultureInfo.InvariantCulture);
+ if (Currency != CurrencyType.None)
+ amount += " " + Currency.ToString().ToUpperInvariant();
+ return amount;
+ }
+
+
+
+ // extensions
+
+ /// <summary>
+ /// Rounds the specified amount to the specified amount of decimals.
+ /// </summary>
+ /// <param name="decimals">The decimals.</param>
+ /// <returns>rounded instance</returns>
+ [Pure]
+ public CurrencyAmount Round(int decimals)
+ {
+ return new CurrencyAmount(Currency, Math.Round(Value, decimals));
+ }
+
+ /// <summary>
+ /// Converts this aurrency amount by applying the specified
+ /// transformation function to the amount and returning new result instance.
+ /// </summary>
+ /// <param name="conversion">The conversion.</param>
+ /// <returns>new result instance</returns>
+ [Pure]
+ public CurrencyAmount Convert(Func<decimal, decimal> conversion)
+ {
+ return new CurrencyAmount(Currency, conversion(Value));
+ }
+
+
+ /// <summary>
+ /// Implements the operator /.
+ /// </summary>
+ /// <param name="originalValue">The original value.</param>
+ /// <param name="value">The value.</param>
+ /// <returns>
+ /// The result of the operator.
+ /// </returns>
+ public static CurrencyAmount operator /(CurrencyAmount originalValue, decimal value)
+ {
+ return new CurrencyAmount(originalValue.Currency, originalValue.Value / value);
+ }
+
+ public static decimal operator /(CurrencyAmount left, CurrencyAmount right)
+ {
+ ThrowIfMismatch(left, right, "/");
+ return left.Value / right.Value;
+ }
+
+
+ /// <summary>
+ /// Implements the operator *.
+ /// </summary>
+ /// <param name="originalValue">The original value.</param>
+ /// <param name="value">The value.</param>
+ /// <returns>
+ /// The result of the operator.
+ /// </returns>
+ public static CurrencyAmount operator *(CurrencyAmount originalValue, decimal value)
+ {
+ return new CurrencyAmount(originalValue.Currency, originalValue.Value * value);
+ }
+
+ /// <summary>
+ /// Implements the operator +.
+ /// </summary>
+ /// <param name="originalValue">The original value.</param>
+ /// <param name="value">The value.</param>
+ /// <returns>
+ /// The result of the operator.
+ /// </returns>
+ public static CurrencyAmount operator +(CurrencyAmount originalValue, decimal value)
+ {
+ return new CurrencyAmount(originalValue.Currency, originalValue.Value + value);
+ }
+
+ /// <summary>
+ /// Implements the operator +.
+ /// </summary>
+ /// <param name="originalValue">The original value.</param>
+ /// <param name="amount">The amount to add.</param>
+ /// <returns>
+ /// The result of the operator.
+ /// </returns>
+ /// ///
+ /// <exception cref="InvalidOperationException">
+ /// If currency types do not match
+ /// </exception>
+ public static CurrencyAmount operator +(CurrencyAmount originalValue, CurrencyAmount amount)
+ {
+ ThrowIfMismatch(originalValue, amount, "+");
+ return new CurrencyAmount(originalValue.Currency, originalValue.Value + amount.Value);
+ }
+
+
+ /// <summary>
+ /// Implements the operator -.
+ /// </summary>
+ /// <param name="originalValue">The original value.</param>
+ /// <param name="amount">
+ /// The amount to subtract.
+ /// </param>
+ /// <returns>
+ /// The result of the operator.
+ /// </returns>
+ /// <exception cref="InvalidOperationException">
+ /// If currency types do not match
+ /// </exception>
+ public static CurrencyAmount operator -(CurrencyAmount originalValue, CurrencyAmount amount)
+ {
+ ThrowIfMismatch(originalValue, amount, "-");
+ return new CurrencyAmount(originalValue.Currency, originalValue.Value - amount.Value);
+ }
+
+
+ /// <summary>
+ /// Implements the operator &gt;.
+ /// </summary>
+ /// <param name="originalValue">The original value.</param>
+ /// <param name="amount">
+ /// The amount to compare with.
+ /// </param>
+ /// <returns>
+ /// The result of the operator.
+ /// </returns>
+ /// <exception cref="InvalidOperationException">
+ /// If currency types do not match
+ /// </exception>
+ public static bool operator >(CurrencyAmount originalValue, CurrencyAmount amount)
+ {
+ ThrowIfMismatch(originalValue, amount, ">");
+ return originalValue.Value > amount.Value;
+ }
+
+
+ /// <summary>
+ /// Implements the operator &gt;=.
+ /// </summary>
+ /// <param name="originalValue">The original value.</param>
+ /// <param name="amount">
+ /// The amount to compare with.
+ /// </param>
+ /// <returns>
+ /// The result of the operator.
+ /// </returns>
+ /// <exception cref="InvalidOperationException">
+ /// If currency types do not match
+ /// </exception>
+ public static bool operator >=(CurrencyAmount originalValue, CurrencyAmount amount)
+ {
+ ThrowIfMismatch(originalValue, amount, ">=");
+ return originalValue.Value >= amount.Value;
+ }
+
+ /// <summary>
+ /// Implements the operator &lt;=.
+ /// </summary>
+ /// <param name="originalValue">The original value.</param>
+ /// <param name="amount">
+ /// The amount to compare with.
+ /// </param>
+ /// <returns>
+ /// The result of the operator.
+ /// </returns>
+ /// <exception cref="InvalidOperationException">
+ /// If currency types do not match
+ /// </exception>
+ public static bool operator <=(CurrencyAmount originalValue, CurrencyAmount amount)
+ {
+ ThrowIfMismatch(originalValue, amount, "<=");
+ return originalValue.Value <= amount.Value;
+ }
+
+ /// <summary>
+ /// Implements the operator &lt;.
+ /// </summary>
+ /// <param name="originalValue">The original value.</param>
+ /// <param name="amount">
+ /// The amount to subtract.
+ /// </param>
+ /// <returns>
+ /// The result of the operator.
+ /// </returns>
+ /// <exception cref="InvalidOperationException">
+ /// If currency types do not match
+ /// </exception>
+ public static bool operator <(CurrencyAmount originalValue, CurrencyAmount amount)
+ {
+ ThrowIfMismatch(originalValue, amount, "<");
+ return originalValue.Value < amount.Value;
+ }
+
+ /// <summary>
+ /// Implements the operator ==.
+ /// </summary>
+ /// <param name="originalValue">The original value.</param>
+ /// <param name="amount">The amount.</param>
+ /// <returns>
+ /// The result of the operator.
+ /// </returns>
+ public static bool operator ==(CurrencyAmount originalValue, CurrencyAmount amount)
+ {
+ return originalValue.Equals(amount);
+ }
+
+ /// <summary>
+ /// Implements the operator !=.
+ /// </summary>
+ /// <param name="originalValue">The original value.</param>
+ /// <param name="amount">The amount.</param>
+ /// <returns>
+ /// The result of the operator.
+ /// </returns>
+ public static bool operator !=(CurrencyAmount originalValue, CurrencyAmount amount)
+ {
+ return !(originalValue == amount);
+ }
+
+
+ /// <summary>
+ /// Implements the operator -.
+ /// </summary>
+ /// <param name="originalValue">The original value.</param>
+ /// <returns>
+ /// The result of the operator.
+ /// </returns>
+ public static CurrencyAmount operator -(CurrencyAmount originalValue)
+ {
+ return new CurrencyAmount(originalValue.Currency, -originalValue.Value);
+ }
+ [DebuggerNonUserCode]
+ static void ThrowIfMismatch(CurrencyAmount originalValue, CurrencyAmount amount, string operationName)
+ {
+ if (originalValue.Currency == amount.Currency)
+ return;
+ var txt = string.Format("Can't apply the '{0}' operation to mismatching currencies '{1}' and '{2}'",
+ operationName, originalValue.Currency, amount.Currency);
+
+ throw new InvalidOperationException(txt);
+ }
+ }
+}
View
39 E016-value-objects/sample-csharp/CurrencyAmountExtendsDecimal.cs
@@ -0,0 +1,39 @@
+using System;
+
+namespace E016_value_objects
+{
+ /// <summary>
+ /// Extensions around
+ /// <see cref="CurrencyAmount" />
+ /// </summary>
+ public static class CurrencyAmountExtendsDecimal
+ {
+ /// <summary>
+ /// Converts the specified deimal to a currency.
+ /// </summary>
+ /// <param name="value">The value.</param>
+ /// <param name="currency">The currency.</param>
+ /// <returns>
+ /// new instance of the currency amount
+ /// </returns>
+ public static CurrencyAmount In(this decimal value, CurrencyType currency)
+ {
+ return new CurrencyAmount(currency, value);
+ }
+
+ public static CurrencyAmount Eur(this decimal value)
+ {
+ return new CurrencyAmount(CurrencyType.Eur, value);
+ }
+
+ public static CurrencyAmount Rub(this decimal value)
+ {
+ return new CurrencyAmount(CurrencyType.Rub, value);
+ }
+
+ public static CurrencyAmount In(this double value, CurrencyType currency)
+ {
+ return new CurrencyAmount(currency, Convert.ToDecimal(value));
+ }
+ }
+}
View
53 E016-value-objects/sample-csharp/E016.value-objects.csproj
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{8689D272-171B-4F25-8ACD-5554532B024F}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>E016</RootNamespace>
+ <AssemblyName>E016</AssemblyName>
+ <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Runtime.Serialization" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="CurrencyAmount.cs" />
+ <Compile Include="CurrencyAmountExtendsDecimal.cs" />
+ <Compile Include="Program.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Folder Include="Properties\" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
View
20 E016-value-objects/sample-csharp/Program.cs
@@ -0,0 +1,20 @@
+using System;
+using E016_value_objects;
+
+namespace E016
+{
+ public static class Program
+ {
+ public static void Main()
+ {
+ Console.WriteLine(10m.Eur() + 15m.Eur());
+
+ Console.WriteLine(32m.Rub() > 30m.Rub());
+
+ Console.Read();
+
+
+
+ }
+ }
+}
View
16 _misc/BTWCSharp.sln
@@ -1,6 +1,6 @@

-Microsoft Visual Studio Solution File, Format Version 11.00
-# Visual Studio 2010
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E002-messaging-basics", "..\E002-messaging-basics\sample-csharp\E002-messaging-basics.csproj", "{77CB3924-2281-46A1-A1A2-2818D0F2EB62}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "E001-introduction", "E001-introduction", "{C2AA0795-D5AC-4F98-B59B-EE854F5C7880}"
@@ -44,6 +44,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E017.Domain.Test", "..\E017
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E017.Domain.Test.Console", "..\E017-view-projections\sample-csharp\E017.Domain.Test.Console\E017.Domain.Test.Console.csproj", "{570C9B79-BB76-459C-B320-84163D7155E2}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E016.value-objects", "..\E016-value-objects\sample-csharp\E016.value-objects.csproj", "{8689D272-171B-4F25-8ACD-5554532B024F}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -214,6 +216,16 @@ Global
{570C9B79-BB76-459C-B320-84163D7155E2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{570C9B79-BB76-459C-B320-84163D7155E2}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{570C9B79-BB76-459C-B320-84163D7155E2}.Release|x86.ActiveCfg = Release|Any CPU
+ {8689D272-171B-4F25-8ACD-5554532B024F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8689D272-171B-4F25-8ACD-5554532B024F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8689D272-171B-4F25-8ACD-5554532B024F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {8689D272-171B-4F25-8ACD-5554532B024F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {8689D272-171B-4F25-8ACD-5554532B024F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {8689D272-171B-4F25-8ACD-5554532B024F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8689D272-171B-4F25-8ACD-5554532B024F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8689D272-171B-4F25-8ACD-5554532B024F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {8689D272-171B-4F25-8ACD-5554532B024F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {8689D272-171B-4F25-8ACD-5554532B024F}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

0 comments on commit 9b68543

Please sign in to comment.
Something went wrong with that request. Please try again.