Сколько должен быть OOP_Crazy программист в реальной жизни?
Дано: сложный алгоритм.
switch obj.field1 + "-" + obj.field2
case "A-A":
return Sample(obj){Error = "Type can't be matched"}
case "A-B":
{
if(obj.field3 != null){
var match = otherObjectCollection.FirstOrDefault(otherOb.field2 == obj.field3);
return mathc != null? Sample(obj,match) : Sample(obj){Error="Can't find match"}
}else{use field4...}
}
case "A-C":
{
var related = objectCollection.FirstOrDefault(parent.field4 == obj.field3);
if(related == null)
return Sample(obj){Error = "Can't find parent"}
else {
if(related.field3 != null){
var match = otherObjectCollection.FirstOrDefault(otherOb.field2 == related.field3);
return mathc != null? Samble(obj,match) : Sample(obj){Error="Can't find match"}
}else{ use field 4 ...}
}
}
И так далее. Много хитрых правил. Я пришел к следующему решению:
abstract class AggregationChain{
protected MyObj Obj;
abstract string Type{get;}
abstract Priority Priority{get;}
abstract bool Decide(MyObj obj);
abstract Sample Aggregate(ICollection<MyObj> objects,ICollection<OtherObj> otherobjects);
bool CanDecide(MyObj obj){
if(Decide(obj)){
Obj = obj;
return true;
}
return false;
}
}
Так что теперь я могу иметь ChainLinks например:
class ABAggregationChainLink{
string Type{get{return "A-B"}}
Priority Priority{get{return Priority.High}}
bool Decide(MyObj obj){
return obj.fiel2 != null;
}
Sample Aggregate(ICollection<MyObj> objects,ICollection<OtherObj> otherobjects){
var match = OtherObjectCollection.FirstOrDefault(otherOb.field2 == obj.field3);
return mathc != null? Samble(obj,match) : Sample(obj){Error="Can't find match"}
}
}
В этом примере мне нужно будет создать еще один AB ChainLink, который будет обрабатывать ситуацию "еще". И для всех случаев переключения мне нужно будет создавать разные цепочки ссылок. Это, конечно, увеличивает количество классов и время для реализации, но классы больше подходят для модульного тестирования и, с моей точки зрения, более расширяемы и гибки.
Вопрос:
- Я думаю - может быть, я взволнован Open-Close и "Хорошим программированием", и для реальных приложений лучше просто создать метод с переключателем и позаботиться о повторно используемых деталях?
- У меня начинка что тут может быть лучшее решение?
PS. Это не рабочий код C#, я просто попытался объяснить основную логику.
1 ответ
Анализируя что-то подобное, вы не можете просто посмотреть, что это сейчас.
- Легко ли поддерживать этот алгоритм в будущем?
- Насколько легко добавлять новые правила, не меняя существующую функциональность?
- Насколько легко это проверить?
- Вам нужно добавить новые правила во время выполнения (архитектуры плагинов)?
В 3 случаях с некоторыми, если другие, это, вероятно, не имеет значения в любом случае. В 40 случаях с дополнительными случаями и новым требованием, что все это нуждается в модульном тестировании? Я бы предпочел чистую изоляцию класса по правилу (или, скорее, универсальный класс с лямбда-выражениями, добавляемый в набор правил)
Ваш класс ссылок выглядит хорошо, но вам все равно понадобится класс цепочки, чтобы объединить все ссылки и определить, какую ссылку обрабатывать. Это может закончиться структурой, которая заменит этот оператор switch следующим образом:
Dictionary<String, Dictionary<String, List<Link>>> index;
if (index.ContainsKey(obj.Field1))
{
var subIndex = index[obj.Field1];
if (subIndex.ContainsKey(obj.Field2))
{
var linkList = subIndex[obj.Field2];
foreach(var Link in linkList)
{
if (Link.Decide(obj))
{
return Link.Aggregate(objects, otherObjects);
}
}
}
}
Если вы измените Решает подпись на Func<bool, MyObj>
и совокупность Func<Sample, ICollection<MyObj>,ICollection<OtherObj>>
тогда вы можете добавить правила, такие как
RuleChain.AddRule("A", "A", (o) => true, (objs, others) => Sample(obj){Error = "Type can't be matched"})
RuleChain.AddRule("A", "B",...