-1

I have an Airport class where Plane objects can take off and land, depending on the weather. If it's stormy, they can't take off/land and if it's sunny they can. I have the following Airport constructor:

public string AirportName { get; set; }
public List<Plane> planes;

public Airport(string airportName)
{
    planes = new List<Plane>();
    AirportName = airportName;
    Weather weather = new Weather(); 
}

I have a Weather class that randomises the weather:

public class Weather
{
    public Weather()
    {
    }

    public string Forecast()
    {
        Random random = new Random();
        var weather = random.Next(1, 11);
        if (weather == 1 || weather == 2)
        {
            return "stormy";
        }
        else
        {
            return "sunny";
        }
    }
}

This is how I use the airport in Main:

static void Main()
{
    var gatwick = new Airport("London Gatwick");
}

As the Weather information is provided by a separate class I would like to inject it into Airport as a dependency. However, I am struggling to do this in C# as I'm really new to the language. Would it be something such as: public Airport(string airportName, Weather weather)?

Would be really grateful if someone could help me understand how to inject as a dependency and how to then call this in Main. Thank you!

GSerg
  • 76,472
  • 17
  • 159
  • 346
juemura7
  • 411
  • 9
  • 20

3 Answers3

1

To answer your question about dependency injection, then you've got it right, you would want to require a Weather object to be passed to the constructor in order for an Airport to be instantiated. More often than not, the Weather object is then stored in a Weather property of the Airport.

However, I think in your case, it would make more sense to have something along the lines of an AirportWeatherManager that contains an Airport as well as a Weather object, and controls behavior. Semantically, and conceptually, it doesn't make sense for an airport to require weather to exist. While the two will most often be found together, the weather is not strictly required in order for an airport to exist. Moreover, by making the Weather a property of the airport, you hand over control of the weather itself to the airport, and anyone who's ever had a flight delayed because of a storm can tell you that's not the case.

Inagnikai
  • 281
  • 1
  • 15
  • 2
    I have to hard disagree with this. Manager classes are almost always code smell and a violation of the single responsibility principle. Dependency Injection is definitely the way to go here. The dependency injection framework can manage a singleton instance of Weather and inject it anywhere it is needed. Better still, make a weather service and inject that instead. In this instance knowledge of the weather is absolutely required for the airport to function, since whether or not a plane can take off or land is entirely based on the weather here. – DetectivePikachu Aug 01 '19 at 20:20
  • 2
    An instance of a Weather object being owned by an Airport does not do a good job of modelling the domain, though. The weather is not a characteristic of the Airport, it is a characteristic of the area the airport is in. The airport has to have knowledge of the weather, but its ability to exist is not predicated on the weather itself. Perhaps it could query a `WeatherService` which has access to weather information, if a manager class is insufficient. – Inagnikai Aug 01 '19 at 20:30
  • Thanks for your input! :) If I do go down the dependency injection route, how do I amend the airport in `static void Main`? This doesn't seem to be working: `var gatwick = new Airport("London Gatwick", Weather weather);` – juemura7 Aug 01 '19 at 20:51
  • @jordantomiko You aren't instantiating your weather object correctly. You have to create a `new Weather()` in the body of your Main and pass the object itself into the constructor of your Airport – Inagnikai Aug 02 '19 at 12:01
  • 1
    I would disagree that injecting Weather as a dependency means that it is owned by the Airport. Its just as the name implies - it is a dependency. Its ability to exist isn't predicated on the weather, but it is a given that weather will always exist when where there is an airport. I agree with using a `WeatherService`, but then you're just injecting that as a dependency and all you have really done is create a layer of abstraction to make yourself feel better about it, ultimately you're still predicating airport operations on the weather its just coming through a 3rd party now – DetectivePikachu Aug 02 '19 at 13:39
  • I still have to point back to the semantics of it all. To tie it into functionality, there are a lot of things that an Airport can "do" without the weather, if we're following that paradigm. An airport can gas up its planes, make a new deal for a restaurant, install a new moving walkway, all without needing to know anything about the weather. It only needs to know the weather when it's actually coordinating flights. With that in mind, a Weather object can be passed to whatever hypothetical `UpdateDepartures` method is being used, since that information is needed for that operation. – Inagnikai Aug 02 '19 at 13:57
1

I recommend you to use a dependency injection library such as Ninject.

You then have to instantiate your class using the Dependency Resolver.

private readonly Weather weather;
public string AirportName { get; set; }
public List<Plane> planes;    

public Airport(string airportName, Weather weather)
{
    planes = new List<Plane>();
    AirportName = airportName;
    weather = weather; 
}

Then in your main class

   IKernel kernel = new StandardKernel();
   kernel.Bind<Airport>().
    To<Airport>().
    WithConstructorArgument("airportName", "Houston Airport");
   var warrior = kernel.Get<Airport>();

Or, if you want to specify the argument when you instantiate, remove the "WithConstructorArgument" from above and do the following:

   kernel.Get<Airport>( new ConstructorArgument( "airportName", "Houston Airport") );
Bruno Farias
  • 785
  • 8
  • 22
0

You're on the right track. Making your constructor take all the dependencies as parameters is called constructor injection. This approach is preferred whenever possible. The other form of DI is called property or field injection. It's only used when the object you're constructing is part of a framework that requires it to have a no-args constructor. For example, an Activity in Android must have a no-arg constructor, so a DI tool like Dagger 2 uses field injection to set public fields.

StackOverthrow
  • 1,158
  • 11
  • 23