Não há matança como exagero
Primeiro de tudo, querido GiMmEtHaCoDeZ, vamos tentar dividir sua tarefa:
- Leia os números
- Classificá-los
- Saída os números classificados.
Como "Dividir e conquistar" é uma estratégia muito importante ao trabalhar com problemas de software, vamos enfrentá-los um por vez
1. Leitura
Outra questão importante no software é a versatilidade. Como não está especificado como o usuário digitará os números, isso pode acontecer por meio do console, por um arquivo, por um serviço da web etc. etc. Talvez até algum método que não possamos pensar no momento. Portanto, é importante que nossa solução possa acomodar vários tipos de entrada. A maneira mais fácil de conseguir isso é extrair a parte importante de uma interface, digamos
public interface IDoubleArrayReader
{
IEnumerable<double> GetDoubles();
DoubleArrayReaderType Type {get;}
}
onde DoubleArrayReaderType
é uma enumeração fornecida com
public enum DoubleArrayReaderType
{
Console,
File,
Database,
Internet,
Cloud,
MockService
}
Também é importante tornar o software testável desde o início, para que uma implementação da interface seja
public class MockServiceDoubleArrayReader : IDoubleArrayReader
{
IEnumerable<double> IDoubleArrayReader.GetDoubles()
{
Random r = new Random();
for(int i =0; i<=10; i++)
{
yield return r.NextDouble();
}
}
DoubleArrayReaderType IDoubleArrayReader.Type
{
get
{
return DoubleArrayReaderType.MockService;
}
}
}
A seguir, a questão lógica é como saberemos carregar o apropriado IDoubleArrayReader
no código. Isso é fácil, desde que usemos uma fábrica simples:
public static class DoubleArrayInputOutputFactory
{
private static Dictionary<DoubleArrayReaderType, IDoubleArrayReader> readers;
static DoubleArrayInputOutputFactory()
{
readers = new Dictionary<DoubleArrayReaderType, IDoubleArrayReader>();
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
{
try
{
var instance = Activator.CreateInstance(type);
if (instance is IDoubleArrayReader)
{
readers.Add((instance as IDoubleArrayReader).Type,
(instance as IDoubleArrayReader));
}
}
catch
{
continue;
}
}
}
public static IDoubleArrayReader CreateDoubleArrayReader(DoubleArrayReaderType type)
{
return readers[type];
}
}
Observe que usamos a reflexão para carregar todos os leitores ativos, para que futuras extensões sejam disponibilizadas automaticamente Agora, no corpo principal do código, apenas fazemos:
IDoubleArrayReader reader = DoubleArrayInputOutputFactory
.CreateDoubleArrayReader(DoubleArrayReaderType.MockService);
var doubles = reader.GetDoubles();
2. Processamento (classificação)
Agora precisamos processar, ou seja, classificar os números que adquirimos. Observe que as etapas são completamente independentes uma da outra; portanto, para o subsistema de classificação, não importa como os números foram inseridos. Além disso, o comportamento de classificação também está sujeito a alterações, por exemplo, talvez seja necessário inserir um algoritmo de classificação mais eficiente. Portanto, naturalmente, extrairemos o comportamento de processamento solicitado em uma interface:
public interface IDoubleArrayProcessor
{
IEnumerable<double> ProcessDoubles(IEnumerable<double> input);
DoubleArrayProcessorType Type {get;}
}
public enum DoubleArrayProcessorType
{
Sorter,
Doubler,
Tripler,
Quadrupler,
Squarer
}
E o comportamento de classificação apenas implementará a interface:
public class SorterDoubleArrayProcessor : IDoubleArrayProcessor
{
IEnumerable<double> IDoubleArrayProcessor.ProcessDoubles(IEnumerable<double> input)
{
var output = input.ToArray();
Array.Sort(output);
return output;
}
DoubleArrayProcessorType IDoubleArrayProcessor.Type
{
get
{
return DoubleArrayProcessorType.Sorter;
}
}
}
Obviamente, precisaremos de uma fábrica para carregar e gerenciar as instâncias de processamento.
public static class DoubleArrayProcessorFactory
{
private static Dictionary<DoubleArrayProcessorType, IDoubleArrayProcessor> processors;
static DoubleArrayProcessorFactory()
{
processors = new Dictionary<DoubleArrayProcessorType, IDoubleArrayProcessor>();
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
{
try
{
var instance = Activator.CreateInstance(type);
if (instance is IDoubleArrayProcessor)
{
processors.Add((instance as IDoubleArrayProcessor).Type, (instance as IDoubleArrayProcessor));
}
}
catch
{
continue;
}
}
}
public static IDoubleArrayProcessor CreateDoubleArrayProcessor(DoubleArrayProcessorType type)
{
return processors[type];
}
}
3. Escrevendo a saída
Não há muito o que dizer aqui, pois esse é um processo que reflete a entrada. De fato, poderíamos combinar as fábricas de leitura e escrita em uma única DoubleArrayInputOutputFactory
, assim:
public interface IDoubleArrayWriter
{
void WriteDoublesArray(IEnumerable<double> doubles);
DoubleArrayWriterType Type {get;}
}
public enum DoubleArrayWriterType
{
Console,
File,
Internet,
Cloud,
MockService,
Database
}
public class ConsoleDoubleArrayWriter : IDoubleArrayWriter
{
void IDoubleArrayWriter.WriteDoublesArray(IEnumerable<double> doubles)
{
foreach(double @double in doubles)
{
Console.WriteLine(@double);
}
}
DoubleArrayWriterType IDoubleArrayWriter.Type
{
get
{
return DoubleArrayWriterType.Console;
}
}
}
public static class DoubleArrayInputOutputFactory
{
private static Dictionary<DoubleArrayReaderType, IDoubleArrayReader> readers;
private static Dictionary<DoubleArrayWriterType, IDoubleArrayWriter> writers;
static DoubleArrayInputOutputFactory()
{
readers = new Dictionary<DoubleArrayReaderType, IDoubleArrayReader>();
writers = new Dictionary<DoubleArrayWriterType, IDoubleArrayWriter>();
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
{
try
{
var instance = Activator.CreateInstance(type);
if (instance is IDoubleArrayReader)
{
readers.Add((instance as IDoubleArrayReader).Type, (instance as IDoubleArrayReader));
}
}
catch
{
continue;
}
}
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
{
try
{
var instance = Activator.CreateInstance(type);
if (instance is IDoubleArrayWriter)
{
writers.Add((instance as IDoubleArrayWriter).Type, (instance as IDoubleArrayWriter));
}
}
catch
{
continue;
}
}
}
public static IDoubleArrayReader CreateDoubleArrayReader(DoubleArrayReaderType type)
{
return readers[type];
}
public static IDoubleArrayWriter CreateDoubleArrayWriter(DoubleArrayWriterType type)
{
return writers[type];
}
}
Juntando tudo
Por fim, nosso programa principal usará toda essa grandiosidade que já construímos, portanto o código será apenas:
var doubles = reader.GetDoubles();
doubles = processor.ProcessDoubles(doubles);
writer.WriteDoublesArray(doubles);
onde, por exemplo, podemos definir reader
, writer
e processor
usando
IDoubleArrayReader reader = DoubleArrayInputOutputFactory.CreateDoubleArrayReader(DoubleArrayReaderType.MockService);
IDoubleArrayProcessor processor = DoubleArrayProcessorFactory.CreateDoubleArrayProcessor(DoubleArrayProcessorType.Sorter);
IDoubleArrayWriter writer = DoubleArrayInputOutputFactory.CreateDoubleArrayWriter(DoubleArrayWriterType.Console);