Skip to content

Instantly share code, notes, and snippets.

@samrahimi
Created March 17, 2017 04:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save samrahimi/e708648812c3f94fd9f030d5ad509cf4 to your computer and use it in GitHub Desktop.
Save samrahimi/e708648812c3f94fd9f030d5ad509cf4 to your computer and use it in GitHub Desktop.
C3PO, The Psychic Robot - Coding Exercise for Horoscope.com
/*
* Implementation of the Robot coding exercise using a thread-safe lazy singleton pattern.
* To save time, the 2 chip implementations only support arrays of integers and not other numeric types
* Detailed explanations of design decisions are written as comments throughout this file. Test script is at
* the end.
*
* TESTING INSTRUCTIONS: Build, Run. Test results will be displayed as the output of a console app.
*
* IMPORTANT: Compatibility with Windows or the Visual Studio build environment is expected but not guaranteed.
* This code was developed, built, and tested using Jetbrains "Rider" IDE and the "Mono"
* open source implementation of .NET 4.5 on Linux Mint 18.1 (there were issues getting VS2015 to run in the
* Windows VM on my personal laptop) */
using System;
using System.Collections.Generic;
using System.Linq;
namespace ThePsychicRobot
{
/// <summary>
/// Interface for a chip containing a method that accepts an array of integers and returns an arbitrary object
/// There are more elegant and extensible ways to do this, but not on a one hour coding assignment.
/// Happy to discuss this in more detail...
/// </summary>
public interface IChip
{
object Execute(int[] sourceData);
}
/// <summary>
/// Class definition and implementation of a Robot that accepts swappable chips. IChips, actually.
/// </summary>
public class Robot
{
/*
We implement a Singleton, using lazy instantiation.
Since Robots are big and expensive, don't initialize it until it is called for
The tradeoff is that the first code to reference Robot.Instance will have to wait for the constructor
to construct the instance.
*/
private static Robot _instance;
private static readonly object _roboLock = new object(); //Stores the lock used by Robot.Instance getter
public static Robot Instance
{
//Using double check locking to minimize overhead. We lock the thread to
//prevent a race condition while the static Robot instance is being created.
//But if the instance already exists, no lock is needed.
//See: https://en.wikipedia.org/wiki/Double-checked_locking
get
{
if (_instance == null)
{
lock (_roboLock)
{
if (_instance == null)
_instance = new Robot();
}
}
return _instance;
}
}
//Since we only need to calculate the number of *distinct* types of Chip that have been inserted into the Robot
//and there is no requirement to return the names themselves (or to log any other data), a HashSet
//provides the fastest (atomic, thread-safe) lookup to see if a particular Chip has been inserted before.
private readonly HashSet<Type> _distinctChipTypesUsed;
public int DistinctChipUsageCount
{
get
{
//According to MSDN, a HashSet maintains an internal Count that updates when items are added / removed
//The getter runs in constant time, so there's no need to maintain our own count
return _distinctChipTypesUsed.Count;
}
}
/// <summary>
// Private constructor, called by Robot.Instance getter if the Robot has not been initialized
/// </summary>
private Robot()
{
_distinctChipTypesUsed = new HashSet<Type>();
}
/// <summary>
/// Inserts a chip into the robot. TODO: make thread safe
/// </summary>
/// <param name="chip"></param>
private IChip _chip;
public void InsertChip(IChip chip)
{
this._chip = chip;
var chipType = chip.GetType();
if (!_distinctChipTypesUsed.Contains(chipType))
_distinctChipTypesUsed.Add(chipType);
}
/// <summary>
/// Runs the Execute method in whatever chip is currently inserted. TODO: make thread safe
/// </summary>
/// <returns></returns>
public object ExecuteChipPayload(int[] data)
{
if (_chip == null)
{
throw new InvalidOperationException("No chip detected.");
}
return _chip.Execute(data);
}
}
/// <summary>
/// A chip that the Robot can use to sort an array of integers ascending or descending
/// </summary>
public class ChipOfSorts : IChip
{
private bool _isDescending = false; //Default order is ascending
public bool IsDescending
{
get { return _isDescending; }
set { _isDescending = value; }
}
public object Execute(int[] sourceData)
{
return (_isDescending ? sourceData.OrderBy(x => x).ToArray() : sourceData.OrderBy(x => x).ToArray());
}
}
/// <summary>
/// A chip that the Robot can use to calculate the sum of an array of integers
/// </summary>
public class TotalChip : IChip
{
public TotalChip()
{
}
public object Execute(int[] sourceData)
{
return sourceData.Sum();
}
}
/// <summary>
/// Tests the robot and chips
/// </summary>
internal class Program
{
public static void Main(string[] args)
{
int[] array1 = {9, 1, 8, 3, 4};
int[] array2 = {0, -4, 26};
var ascChip = new ChipOfSorts();
ascChip.IsDescending = false;
var descChip = new ChipOfSorts();
descChip.IsDescending = true;
var totalChip = new TotalChip();
var C3PO = Robot.Instance;
Console.WriteLine("Got Robot Instance");
Console.WriteLine("Testing array1, 1st instance of ChipOfSorts, ascending sort");
C3PO.InsertChip(ascChip);
var sortedAsc = (int[]) C3PO.ExecuteChipPayload(array1);
Console.WriteLine("Expected: 1 3 4 8 9");
foreach (var a in sortedAsc)
{
Console.Write(a+" ");
}
Console.WriteLine("Testing array1, 2nd instance of ChipOfSorts, descending sort");
C3PO.InsertChip(descChip);
var sortedDesc = (int[]) C3PO.ExecuteChipPayload(array1);
Console.WriteLine("Expected: 9 8 4 3 1");
foreach (var a in sortedDesc)
{
Console.Write(a+" ");
}
Console.WriteLine("Testing array2, instance of TotalChip");
C3PO.InsertChip(totalChip);
var sum = (int) C3PO.ExecuteChipPayload(array2);
Console.WriteLine("Expected: 22");
Console.WriteLine(sum);
Console.WriteLine("Testing Robot.DistinctChipUsageCount - Expected Result is 2");
Console.WriteLine(C3PO.DistinctChipUsageCount);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment