-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from wlindley/models
Implemented models for bantam.
- Loading branch information
Showing
7 changed files
with
300 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
namespace Bantam | ||
{ | ||
public interface Model : Poolable | ||
{ | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
using System; | ||
|
||
namespace Bantam | ||
{ | ||
public class ModelCreatedEvent : Event | ||
{ | ||
public Model model; | ||
public Type type; | ||
|
||
public void Reset() | ||
{ | ||
model = null; | ||
type = null; | ||
} | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
using System; | ||
|
||
namespace Bantam | ||
{ | ||
public class ModelDestroyedEvent : Event | ||
{ | ||
public Model model; | ||
public Type type; | ||
|
||
public void Reset() | ||
{ | ||
model = null; | ||
type = null; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.ObjectModel; | ||
|
||
namespace Bantam | ||
{ | ||
public delegate void ModelInitializer<T>(T model) where T : Model; | ||
|
||
public class ModelRegistry | ||
{ | ||
private Dictionary<Type, ModelListWrapper> modelLists; | ||
private EventBus eventBus; | ||
private ObjectPool pool; | ||
|
||
public ModelRegistry(ObjectPool pool, EventBus eventBus) | ||
{ | ||
this.pool = pool; | ||
this.eventBus = eventBus; | ||
modelLists = new Dictionary<Type, ModelListWrapper>(); | ||
} | ||
|
||
public void Create<T>(ModelInitializer<T> initializer = null) where T : class, Model, new() | ||
{ | ||
EnsureTypeExists<T>(); | ||
(modelLists[typeof(T)] as ModelListWrapper<T>).CreateModel(initializer); | ||
} | ||
|
||
public IEnumerable<T> Get<T>() where T : class, Model, new() | ||
{ | ||
EnsureTypeExists<T>(); | ||
return modelLists[typeof(T)].GetModels<T>(); | ||
} | ||
|
||
public void Destroy<T>(T model) where T : class, Model, new() | ||
{ | ||
EnsureTypeExists<T>(); | ||
(modelLists[typeof(T)] as ModelListWrapper<T>).DestroyModel(model); | ||
} | ||
|
||
private void EnsureTypeExists<T>() where T : class, Model, new() | ||
{ | ||
var type = typeof(T); | ||
if (!modelLists.ContainsKey(type)) | ||
modelLists[type] = new ModelListWrapper<T>(pool, eventBus); | ||
} | ||
|
||
private interface ModelListWrapper | ||
{ | ||
IEnumerable<T> GetModels<T>() where T : class, Model, new(); | ||
} | ||
|
||
private class ModelListWrapper<T> : ModelListWrapper where T : class, Model, new() | ||
{ | ||
private List<T> models; | ||
private ObjectPool pool; | ||
private EventBus eventBus; | ||
|
||
public ModelListWrapper(ObjectPool pool, EventBus eventBus) | ||
{ | ||
this.pool = pool; | ||
this.eventBus = eventBus; | ||
models = new List<T>(); | ||
} | ||
|
||
public IEnumerable<U> GetModels<U>() where U : class, Model, new() | ||
{ | ||
return models as IEnumerable<U>; | ||
} | ||
|
||
public void CreateModel(ModelInitializer<T> initializer = null) | ||
{ | ||
var instance = pool.Allocate<T>(); | ||
if (null != initializer) | ||
initializer(instance); | ||
models.Add(instance); | ||
eventBus.Dispatch<ModelCreatedEvent>(evt => { | ||
evt.model = instance; | ||
evt.type = typeof(T); | ||
}); | ||
} | ||
|
||
public void DestroyModel(T model) | ||
{ | ||
if (null == model) | ||
throw new NullModelException(); | ||
if (!models.Contains(model)) | ||
return; | ||
models.Remove(model); | ||
eventBus.Dispatch<ModelDestroyedEvent>(evt => { | ||
evt.model = model; | ||
evt.type = typeof(T); | ||
}); | ||
pool.Free(model); | ||
} | ||
} | ||
} | ||
|
||
public class ModelRegistryException : Exception {} | ||
public class NullModelException : ModelRegistryException {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
using NUnit.Framework; | ||
|
||
namespace Bantam.Test | ||
{ | ||
public class ModelRegistryTest | ||
{ | ||
private ModelRegistry testObj; | ||
private EventBus eventBus; | ||
|
||
[SetUp] | ||
public void SetUp() | ||
{ | ||
var pool = new ObjectPool(); | ||
eventBus = new EventBus(pool); | ||
testObj = new ModelRegistry(pool, eventBus); | ||
} | ||
|
||
[Test] | ||
public void CreatingModelAllowsItToBeRetrieved() | ||
{ | ||
var modelCount = 0; | ||
testObj.Create<DummyModel>(); | ||
foreach (var model in testObj.Get<DummyModel>()) | ||
modelCount++; | ||
Assert.AreEqual(1, modelCount); | ||
} | ||
|
||
[Test] | ||
public void CreateCallsInitializerWhenProvided() | ||
{ | ||
var modelCount = 0; | ||
var expectedValue = "a random string"; | ||
testObj.Create<DummyModel>((model) => { | ||
model.value = expectedValue; | ||
}); | ||
foreach (var model in testObj.Get<DummyModel>()) | ||
if (expectedValue == model.value) | ||
modelCount++; | ||
Assert.AreEqual(1, modelCount); | ||
} | ||
|
||
[Test] | ||
public void CreateFiresEvent() | ||
{ | ||
var eventFired = false; | ||
var expectedValue = "another random string"; | ||
eventBus.AddListener<ModelCreatedEvent>((evt) => { | ||
var model = (DummyModel)evt.model; | ||
Assert.AreEqual(expectedValue, model.value); | ||
Assert.AreEqual(typeof(DummyModel), evt.type); | ||
eventFired = true; | ||
}); | ||
testObj.Create<DummyModel>((model) => { | ||
model.value = expectedValue; | ||
}); | ||
Assert.IsTrue(eventFired); | ||
} | ||
|
||
[Test] | ||
public void GetReturnsEmptyEnumerableWhenNoModelsCreatedForType() | ||
{ | ||
var wasCalled = false; | ||
foreach (var model in testObj.Get<DummyModel>()) | ||
wasCalled = true; | ||
Assert.IsFalse(wasCalled); | ||
} | ||
|
||
[Test] | ||
public void DestroyCausesSpecifiedModelToNotBeReturnedByGet() | ||
{ | ||
var matchingCount = 0; | ||
var totalCount = 0; | ||
var expectedValue = "yet another random string"; | ||
|
||
DummyModel specifiedModel = null; | ||
testObj.Create<DummyModel>(model => { | ||
model.value = expectedValue; | ||
specifiedModel = model; | ||
}); | ||
testObj.Create<DummyModel>(); | ||
|
||
testObj.Destroy<DummyModel>(specifiedModel); | ||
|
||
foreach (var model in testObj.Get<DummyModel>()) | ||
{ | ||
totalCount++; | ||
if (expectedValue == model.value) | ||
matchingCount++; | ||
} | ||
Assert.AreEqual(0, matchingCount); | ||
Assert.AreEqual(1, totalCount); | ||
} | ||
|
||
[Test] | ||
public void DestroyDoesNothingSilentlyWhenSpecifiedModelIsNotInModelRegistry() | ||
{ | ||
var model = new DummyModel(); | ||
testObj.Destroy<DummyModel>(model); | ||
} | ||
|
||
[Test] | ||
public void DestroyThrowsNullModelExceptionWhenSpecifiedModelIsNull() | ||
{ | ||
Assert.Throws<NullModelException>(() => testObj.Destroy<DummyModel>(null)); | ||
} | ||
|
||
[Test] | ||
public void DestroyFiresEvent() | ||
{ | ||
var eventFired = false; | ||
var expectedValue = "the most random string"; | ||
|
||
DummyModel actualModel = null; | ||
eventBus.AddListener<ModelDestroyedEvent>((evt) => { | ||
var model = (DummyModel)evt.model; | ||
Assert.AreEqual(expectedValue, model.value); | ||
Assert.AreEqual(typeof(DummyModel), evt.type); | ||
eventFired = true; | ||
}); | ||
testObj.Create<DummyModel>((model) => { | ||
model.value = expectedValue; | ||
actualModel = model; | ||
}); | ||
|
||
testObj.Destroy<DummyModel>(actualModel); | ||
|
||
Assert.IsTrue(eventFired); | ||
} | ||
|
||
[Test] | ||
public void DestroyDoesNotFireAnEventWhenSpecifiedModelIsNotInModelRegistry() | ||
{ | ||
var wasCalled = false; | ||
var model = new DummyModel(); | ||
eventBus.AddListener<ModelDestroyedEvent>(evt => { | ||
wasCalled = true; | ||
}); | ||
|
||
testObj.Destroy<DummyModel>(model); | ||
|
||
Assert.IsFalse(wasCalled); | ||
} | ||
} | ||
|
||
public class DummyModel : Model | ||
{ | ||
public string value = ""; | ||
|
||
public void Reset() {} | ||
} | ||
|
||
public class DummyModel2 : Model | ||
{ | ||
public void Reset() {} | ||
} | ||
} |