Skip to content

Instantly share code, notes, and snippets.

@asarnaout
Last active September 28, 2018 02:02
Show Gist options
  • Save asarnaout/26a8d1d27a60499859ef2c83db76cbfc to your computer and use it in GitHub Desktop.
Save asarnaout/26a8d1d27a60499859ef2c83db76cbfc to your computer and use it in GitHub Desktop.
Flyweight & Factory Patterns
public class Client
{
public static void Main(string[] args)
{
var routes = new List<ShipmentRoute>();
var zoneFactory = new ZoneFactory();
var shipmentRouteFactory = new ShipmentRouteFactory();
for (var i = 0; i < 2000000; i++) //Creating 2 million identical routes, just coz I can lol
{
var origin = zoneFactory.GetZone("10010");
var destination = zoneFactory.GetZone("33609");
var shipment = shipmentRouteFactory.GetShipmentRoute(origin, destination);
routes.Add(shipment);
}
}
}
public static class EnumerableExtensions
{
public static IEnumerable<string> RemoveDuplicates(this IEnumerable<string> items)
{
var result = new List<string>();
var set = new HashSet<string>();
foreach(var item in items)
{
if (!set.Contains(item))
{
result.Add(item);
set.Add(item);
}
}
return result;
}
}
public class ShipmentRoute //A mutable domain Entity (Assuming the business wants to track the routes)
{
protected ShipmentRoute()
{
}
public ShipmentRoute(string identifier, Zone origin, Zone destination)
{
Identifier = identifier;
Origin = origin;
Destination = destination;
}
public string Identifier { get; }
public Zone Origin { get; set; }
public Zone Destination { get; set; }
}
public class ShipmentRouteFactory
{
public ShipmentRoute GetShipmentRoute(Zone origin, Zone destination)
{
//Implement some blah business rules here for creating ShipmentRoutes
var identifier = $"{origin.ZipCode.Substring(0, 3)}_" +
$"{destination.ZipCode.Substring(0, 3)}_" +
$"{Guid.NewGuid()}";
if (origin.ZipCode == "33609" &&
destination.ZipCode.StartsWith("900", StringComparison.Ordinal))
{
origin.RegisterCourier("usps");
}
return new ShipmentRoute(identifier, origin, destination);
}
}
public class Zone : IEquatable<Zone> //An immutable flyweight...This represents a domain value object
{
public string ZipCode { get; }
public IReadOnlyCollection<string> AvailableCouriers { get; private set; }
protected Zone()
{
}
public Zone(string zipCode)
{
ZipCode = zipCode;
AvailableCouriers = new List<string>();
}
public Zone(string zipCode, IEnumerable<string> couriers) : this(zipCode)
{
AvailableCouriers = new List<string>(couriers.RemoveDuplicates());
}
public Zone RegisterCourier(string courier)
{
var newCouriers = new List<string>(AvailableCouriers)
{
courier
};
return new Zone(ZipCode, newCouriers);
}
public bool Equals(Zone other)
{
if (other == default(Zone)) return false;
if (ReferenceEquals(this, other)) return true;
if (ZipCode != other.ZipCode) return false;
if (AvailableCouriers.Count != other.AvailableCouriers.Count) return false;
if (!AvailableCouriers.All(x => other.AvailableCouriers.Contains(x))) return false;
return true;
}
}
public class ZoneFactory
{
private readonly IDictionary<string, Zone> _zones = new Dictionary<string, Zone>();
private readonly object _lock = new object();
public Zone GetZone(string zipCode)
{
lock (_lock)
{
if (_zones.TryGetValue(zipCode, out var zone))
{
return zone;
}
if (!int.TryParse(zipCode, out var integerZipCode))
{
throw new NotImplementedException("Unsupported zipcode");
}
//Throwing in some business rules for the Zone creation process within the factory as long they aren't used elsewhere
if (integerZipCode > 33000 && integerZipCode < 35010)
{
zone = new Zone(zipCode, new List<string> { "fedex", "ups" });
}
else
{
zone = new Zone(zipCode);
}
_zones.Add(zipCode, zone);
return zone;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment