Eu começaria não pensando em um gerente de ativos . Pensar na sua arquitetura em termos vagamente definidos (como "gerente") tende a permitir que você varra mentalmente muitos detalhes para baixo do tapete e, consequentemente, fica mais difícil escolher uma solução.
Concentre-se nas suas necessidades específicas, que parecem estar relacionadas à criação de um mecanismo de carregamento de recursos que abstrai o armazenamento de origem subjacente e permite extensibilidade do conjunto de tipos suportado. Não há realmente nada na sua pergunta a respeito, por exemplo, do armazenamento em cache de recursos já carregados - o que é bom, porque, de acordo com o princípio de responsabilidade única, você provavelmente deve criar um cache de ativos como uma entidade separada e agregar as duas interfaces em outros lugares. , como apropriado.
Para abordar sua preocupação específica, você deve projetar seu carregador para que ele não faça o carregamento de nenhum ativo, mas delegue essa responsabilidade a interfaces personalizadas para carregar tipos específicos de ativos. Por exemplo:
interface ITypeLoader {
object Load (Stream assetStream);
}
Você pode criar novas classes que implementam essa interface, com cada nova classe sendo adaptada para carregar um tipo específico de dados de um fluxo. Ao usar um fluxo, o carregador de tipos pode ser gravado em uma interface comum e independente de armazenamento e não precisa ser codificado para carregar do disco ou de um banco de dados; isso permitiria que você carregasse seus ativos a partir de fluxos de rede (o que pode ser muito útil na implementação de recarga a quente de ativos quando o jogo estiver sendo executado em um console e suas ferramentas de edição em um PC conectado à rede).
Seu principal carregador de ativos precisa ser capaz de registrar e rastrear esses carregadores específicos de tipo:
class AssetLoader {
public void RegisterType (string key, ITypeLoader loader) {
loaders[key] = loader;
}
Dictionary<string, ITypeLoader> loaders = new Dictionary<string, ITypeLoader>();
}
A "chave" usada aqui pode ser o que você quiser - e não precisa ser uma string, mas é fácil começar com isso. A chave levará em consideração a forma como você espera que um usuário identifique um ativo específico e será usada para procurar o carregador apropriado. Como você deseja ocultar o fato de que a implementação pode estar usando um sistema de arquivos ou um banco de dados, não é possível ter usuários que se referem a ativos por um caminho do sistema de arquivos ou algo assim.
Os usuários devem se referir a um ativo com um mínimo de informações. Em alguns casos, apenas um nome de arquivo por si só seria suficiente, mas descobri que muitas vezes é desejável usar um par de tipo / nome para que tudo seja muito explícito. Assim, um usuário pode se referir a uma instância nomeada de um dos seus arquivos XML de animação como "AnimationXml","PlayerWalkCycle"
.
Aqui, AnimationXml
seria a chave com a qual você se registrou AnimationXmlLoader
, o que implementa IAssetLoader
. Obviamente, PlayerWalkCycle
identifica o ativo específico. Dado um nome de tipo e um nome de recurso, o carregador de ativos pode consultar seu armazenamento persistente em busca dos bytes brutos desse ativo. Como estamos buscando a máxima generalidade aqui, você pode implementar isso fornecendo ao carregador um meio de acesso ao armazenamento ao criá-lo, permitindo substituir o meio de armazenamento por qualquer coisa que possa fornecer um fluxo posteriormente:
interface IAssetStreamProvider {
Stream GetStream (string type, string name);
}
class AssetLoader {
public AssetLoader (IAssetStreamProvider streamProvider) {
provider = streamProvider;
}
object LoadAsset (string type, string name) {
var loader = loaders[type];
var stream = provider.GetStream(type, name);
return loader.Load(stream);
}
public void RegisterType (string type, ITypeLoader loader) {
loaders[type] = loader;
}
IAssetStreamProvider provider;
Dictionary<string, ITypeLoader> loaders = new Dictionary<string, ITypeLoader>();
}
Um provedor de fluxo muito simples simplesmente procuraria em um diretório raiz de ativos especificado um subdiretório nomeado type
e carregaria os bytes brutos do arquivo nomeado name
em um fluxo e o retornaria.
Em resumo, o que você tem aqui é um sistema em que:
- Há uma classe que sabe ler bytes não processados de algum tipo de armazenamento de back-end (um disco, um banco de dados, um fluxo de rede, qualquer que seja).
- Existem classes que sabem como transformar um fluxo de bytes brutos em um tipo específico de recurso e devolvê-lo.
- O seu "carregador de ativos" real apenas possui uma coleção dos itens acima e sabe como canalizar a saída do provedor de fluxo para o carregador específico do tipo e, assim, produzir um ativo concreto. Ao expor maneiras de configurar o provedor de fluxo e os carregadores específicos do tipo, você tem um sistema que pode ser estendido pelos clientes (ou por você) sem precisar modificar o código do carregador de ativos real.
Algumas advertências e notas finais:
O código acima é basicamente C #, mas deve ser traduzido para praticamente qualquer idioma com o mínimo de esforço. Para facilitar isso, omiti muitas coisas, como verificação de erros ou uso adequado IDisposable
e outros idiomas que podem não se aplicar diretamente em outros idiomas. Esses são deixados como lição de casa para o leitor.
Da mesma forma, retorno o ativo concreto como object
acima, mas você pode usar genéricos ou modelos ou qualquer outra coisa para produzir um tipo de objeto mais específico, se quiser (você deve, é bom trabalhar com ele).
Como acima, eu não lido com cache aqui. No entanto, você pode adicionar o armazenamento em cache facilmente e com o mesmo tipo de generalidade e configurabilidade. Experimente e veja!
Há muitas, muitas e muitas maneiras de fazer isso, e certamente não há uma maneira ou consenso, e é por isso que você não conseguiu encontrar uma. Tentei fornecer código suficiente para transmitir os pontos específicos sem transformar essa resposta em uma parede de código dolorosamente longa. Já é extremamente longo como é. Se você tiver perguntas esclarecedoras, sinta-se à vontade para comentar ou me encontrar no chat .