Skip to content

Commit

Permalink
Re-work the Visitor pattern.
Browse files Browse the repository at this point in the history
Add models for KB, MB, GB, TB, PT.
Add converters.
Update the solution structure.
  • Loading branch information
eminencegrs committed Jan 31, 2024
1 parent dac2335 commit 538e777
Show file tree
Hide file tree
Showing 31 changed files with 333 additions and 148 deletions.
30 changes: 30 additions & 0 deletions Behavioral/DesignPatterns.Visitor/Context.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// using AutoFixture;
//
// namespace DesignPatterns.Visitor;
//
// public class Context
// {
// private readonly IFixture fixture = new Fixture();
// private readonly Random random = new Random();
//
// public (ICollection<IAmount> itemsInBytes, ICollection<IAmount> itemsInGB) GetData()
// {
// var inBytes = GetSizesInBytes().ToList();
// var inGB = GetSizesInGB().ToList();
// return (inBytes, inGB);
// }
//
// private static IEnumerable<IAmount> GetSizesInBytes()
// {
// yield return new AmountInBytes { Value = 1073741824 };
// yield return new AmountInBytes { Value = 2147483648 };
// yield return new AmountInBytes { Value = 3221225472 };
// }
//
// private static IEnumerable<IAmount> GetSizesInGB()
// {
// yield return new AmountInGB { Value = 10 };
// yield return new AmountInGB { Value = 20 };
// yield return new AmountInGB { Value = 30 };
// }
// }
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using DesignPatterns.Visitor.Models;

namespace DesignPatterns.Visitor.Converters;

public class GigaBytesToBytesConverter : ISizeConverter<ISize>
{
public ISize Convert(ISize size)
{
if (size is SizeInGigaBytes gigaBytes)
{
return new SizeInBytes
{
Value = gigaBytes.Value * 1024 * 1024 * 1024
};
}

throw new InvalidOperationException("Could not convert GigaBytes to Bytes.");
}

public bool CanConvert(ISize size)
{
return size is SizeInGigaBytes;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using DesignPatterns.Visitor.Models;

namespace DesignPatterns.Visitor.Converters;

public class KiloBytesToBytesConverter : ISizeConverter<ISize>
{
public ISize Convert(ISize size)
{
if (size is SizeInKiloBytes kiloBytes)
{
return new SizeInBytes
{
Value = kiloBytes.Value * 1024
};
}

throw new InvalidOperationException("Could not convert KiloBytes to Bytes.");
}

public bool CanConvert(ISize size)
{
return size is SizeInKiloBytes;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using DesignPatterns.Visitor.Models;

namespace DesignPatterns.Visitor.Converters;

public class MegaBytesToBytesConverter : ISizeConverter<ISize>
{
public ISize Convert(ISize size)
{
if (size is SizeInMegaBytes megaBytes)
{
return new SizeInBytes
{
Value = megaBytes.Value * 1024 * 1024
};
}

throw new InvalidOperationException("Could not convert MegaBytes to Bytes.");
}

public bool CanConvert(ISize size)
{
return size is SizeInMegaBytes;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using DesignPatterns.Visitor.Models;

namespace DesignPatterns.Visitor.Converters;

public class PetaBytesToBytesConverter : ISizeConverter<ISize>
{
public ISize Convert(ISize size)
{
if (size is SizeInPetaBytes petaBytes)
{
return new SizeInBytes
{
Value = petaBytes.Value * 1024 * 1024 * 1024 * 1024 * 1024
};
}

throw new InvalidOperationException("Could not convert PetaBytes to Bytes.");
}

public bool CanConvert(ISize size)
{
return size is SizeInPetaBytes;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using DesignPatterns.Visitor.Models;

namespace DesignPatterns.Visitor.Converters;

public class TeraBytesToBytesConverter : ISizeConverter<ISize>
{
public ISize Convert(ISize size)
{
if (size is SizeInTeraBytes gigaBytes)
{
return new SizeInBytes
{
Value = gigaBytes.Value * 1024 * 1024 * 1024 * 1024
};
}

throw new InvalidOperationException("Could not convert TeraBytes to Bytes.");
}

public bool CanConvert(ISize size)
{
return size is SizeInTeraBytes;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,6 @@
<RootNamespace>DesignPatterns.Visitor</RootNamespace>
</PropertyGroup>

<ItemGroup>
<Content Include="..\.dockerignore">
<Link>.dockerignore</Link>
</Content>
</ItemGroup>

<ItemGroup>
<PackageReference Include="AutoFixture" Version="4.18.1" />
</ItemGroup>
Expand Down
15 changes: 15 additions & 0 deletions Behavioral/DesignPatterns.Visitor/Handler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// namespace DesignPatterns.Visitor;
//
// public class Handler(IEnumerable<IVisitor> visitors)
// {
// public IEnumerable<IAmount> Process(ICollection<IAmount> sizes)
// {
// var result = new List<IAmount>();
// foreach (var visitor in visitors)
// {
// result.AddRange(sizes.Select(size => size.Accept(visitor)));
// }
//
// return result;
// }
// }
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ public interface ISize
{
double Value { get; }
string Unit { get; }
ISize Accept(IVisitor visitor);
ISize Accept(IVisitor<ISize, ISize> visitor);
}
7 changes: 7 additions & 0 deletions Behavioral/DesignPatterns.Visitor/ISizeConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace DesignPatterns.Visitor;

public interface ISizeConverter<in T> where T : ISize
{
ISize Convert(T size);
bool CanConvert(T size);
}
6 changes: 6 additions & 0 deletions Behavioral/DesignPatterns.Visitor/IVisitor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace DesignPatterns.Visitor;

public interface IVisitor<in TInput, out TResult>
{
TResult Visit(TInput size);
}
12 changes: 12 additions & 0 deletions Behavioral/DesignPatterns.Visitor/Models/Size.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace DesignPatterns.Visitor.Models;

public record class Size : ISize
{
public double Value { get; init; }
public virtual string Unit { get; init; }

Check warning on line 6 in Behavioral/DesignPatterns.Visitor/Models/Size.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Unit' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 6 in Behavioral/DesignPatterns.Visitor/Models/Size.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Unit' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

public ISize Accept(IVisitor<ISize, ISize> visitor)
{
return visitor.Visit(this);
}
}
6 changes: 6 additions & 0 deletions Behavioral/DesignPatterns.Visitor/Models/SizeInBytes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace DesignPatterns.Visitor.Models;

public record class SizeInBytes : Size
{
public override string Unit => "Bytes";
}
6 changes: 6 additions & 0 deletions Behavioral/DesignPatterns.Visitor/Models/SizeInGigaBytes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace DesignPatterns.Visitor.Models;

public record class SizeInGigaBytes : Size
{
public override string Unit => "GB";
}
6 changes: 6 additions & 0 deletions Behavioral/DesignPatterns.Visitor/Models/SizeInKiloBytes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace DesignPatterns.Visitor.Models;

public record class SizeInKiloBytes : Size
{
public override string Unit => "KB";
}
6 changes: 6 additions & 0 deletions Behavioral/DesignPatterns.Visitor/Models/SizeInMegaBytes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace DesignPatterns.Visitor.Models;

public record class SizeInMegaBytes : Size
{
public override string Unit => "MB";
}
6 changes: 6 additions & 0 deletions Behavioral/DesignPatterns.Visitor/Models/SizeInPetaBytes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace DesignPatterns.Visitor.Models;

public record class SizeInPetaBytes : Size
{
public override string Unit => "PB";
}
6 changes: 6 additions & 0 deletions Behavioral/DesignPatterns.Visitor/Models/SizeInTeraBytes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace DesignPatterns.Visitor.Models;

public record class SizeInTeraBytes : Size
{
public override string Unit => "TB";
}
41 changes: 41 additions & 0 deletions Behavioral/DesignPatterns.Visitor/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using DesignPatterns.Visitor;
using DesignPatterns.Visitor.Converters;

// var context = new Context();
// var discountVisitor = new SizeVisitor();
// var handler = new Handler(new[] { discountVisitor });
// var data = context.GetData();
// var convertedInGB = handler.Process(data.itemsInBytes);
// var convertedInBytes = handler.Process(data.itemsInGB);
//
// Console.WriteLine();

var strings = new List<string>
{
"100KB",
"200 KB",
"10 MB",
"200MB",
"1GB",
"20 GB"
};

var visitor = new StringVisitor();

var result = new List<ISize>();
result.AddRange(strings.Select(x => x.Accept(visitor)));

var amountVisitor = new SizeVisitor(
new List<ISizeConverter<ISize>>
{
new KiloBytesToBytesConverter(),
new MegaBytesToBytesConverter(),
new GigaBytesToBytesConverter(),
new TeraBytesToBytesConverter(),
new PetaBytesToBytesConverter(),
});

var newResult = new List<ISize>();
newResult.AddRange(result.Select(x => x.Accept(amountVisitor)));

Console.WriteLine();
19 changes: 19 additions & 0 deletions Behavioral/DesignPatterns.Visitor/SizeVisitor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace DesignPatterns.Visitor;

public class SizeVisitor : IVisitor<ISize, ISize>
{
private readonly IEnumerable<ISizeConverter<ISize>> converters;

public SizeVisitor(IEnumerable<ISizeConverter<ISize>> converters)
{
this.converters = converters;
}

public ISize Visit(ISize size)
{
var converter = this.converters.FirstOrDefault(x => x.CanConvert(size));
return converter is null
? throw new InvalidOperationException("Could not find a proper converter.")
: converter.Convert(size);
}
}
9 changes: 9 additions & 0 deletions Behavioral/DesignPatterns.Visitor/StringExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace DesignPatterns.Visitor;

public static class StringExtensions
{
public static ISize Accept(this string value, IVisitor<string, ISize> visitor)
{
return visitor.Visit(value);
}
}
36 changes: 36 additions & 0 deletions Behavioral/DesignPatterns.Visitor/StringVisitor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Text.RegularExpressions;
using DesignPatterns.Visitor.Models;

namespace DesignPatterns.Visitor;

public class StringVisitor : IVisitor<string, ISize>
{
public ISize Visit(string size)
{
if (string.IsNullOrWhiteSpace(size))
{
throw new ArgumentException("Input size string cannot be null or empty.", nameof(size));
}

var regexPattern = @"^(?<value>\d+(\.\d+)?)\s*(?<unit>[KMGTP]?B)$";
var match = Regex.Match(size, regexPattern, RegexOptions.IgnoreCase);

if (!match.Success)
{
throw new ArgumentException("Invalid size format.", nameof(size));
}

var value = long.Parse(match.Groups["value"].Value);
var unit = match.Groups["unit"].Value.ToUpper();

return unit switch
{
"KB" => new SizeInKiloBytes { Value = value },
"MB" => new SizeInMegaBytes { Value = value },
"GB" => new SizeInGigaBytes { Value = value },
"TB" => new SizeInTeraBytes { Value = value },
"PB" => new SizeInPetaBytes { Value = value },
_ => throw new ArgumentOutOfRangeException(nameof(size), "Unknown size unit.")
};
}
}
Loading

0 comments on commit 538e777

Please sign in to comment.