0
\$\begingroup\$

I'm writing a C# library that's a wrapper around the Yahoo Finance YQL API. I'm stuck on how I want to expose my API to consumers. My design thus far consists of separate interfaces for the different parts of the Yahoo Finance API:

public interface IYahooFinanceCharts {
    Task<Image> GetChartAsync(string tickerSymbol, ChartSize size, ChartTimeSpan timeSpan);
}

public interface IYahooFinanceQuotes {
    Task<IEnumerable<Quote>> GetQuotesAsync(params string[] tickerSymbols);
}

public interface IYahooFinanceStocks {
    Task<IEnumerable<Stock>> GetStocksAsync(params string[] tickerSymbols);
}

public interface IYahooFinanceTickerSymbols {
    Task<IEnumerable<TickerSymbol>> SearchAsync(string symbolOrKeyword);
}

I have internal classes that implement these interfaces to handle the details of building a request for the YQL API and parsing the result of the response. I don't want to expose these classes to consumers.

I was thinking of using a facade to reduce the main entry point into my API to one class. Consumers could cast, instantiate, or register (DI container) the facade using the various interfaces to zero-in on the parts they're interested in.

public class YahooFinance : IYahooFinanceCharts, IYahooFinanceQuotes, IYahooFinanceStocks, IYahooFinanceTickerSymbols
{
    // Delegating calls to the internal classes that implement these interfaces...
}

...

IYahooFinanceStocks stocks = new YahooFinance();
var results = await stocks.GetStocksAsync("MSFT", "GOOG");

I'm not sure if I like this because if consumers simply use

var yahooFinance = new YahooFinance();

the entire API is exposed to them, defeating the purpose of the separate interfaces. On the other hand, not using a facade and forcing consumers to instantiate the concrete class for each interface is clumsy, increases the surface area of the API, and I'd have to expose the concrete types...

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

What you're looking for is actually the factory pattern.

Your implementation could be something like

public class YahooFinanceFactory{

   public IYahooFinanceQuotes GetQuotes()
   {
     return new YahooFinanceQuotesConreteImplementation();
   }

[...]

}

The concrete implementations are still marked as internal so they are not exposed.

Alternatively, you could use reflection for this.

public static T ResolveConcreteType<T>()
{
    return (from t in Assembly.GetExecutingAssembly().GetTypes()
            where t.GetInterfaces().Contains(typeof(T))
            && t.IsClass //Find the classes that implement the interface we want
            select (T)Activator.CreateInstance(t, null))
        .SingleOrDefault();

}

Usage:

var quotes = YahooFinanceFactory.ResolveConcreteType<IYahooQuotes>();

This makes the assumption that your concrete types support empty constructors however.

\$\endgroup\$
1
  • \$\begingroup\$ My concretes types do have empty constructors so that's not a problem. My concern with this design is that it's duplicating the work of a DI container (i.e. resolving concrete types from interfaces) and is essentially a service locator. \$\endgroup\$
    – Brett
    May 20, 2014 at 22:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.