Skip to main content
added 1545 characters in body
Source Link

But you're right that the "factory of factories" characterization is not exactly right (it's missleadingmisleading). Depending on what you mean by "different assembly lines" (and I can't be entirely sure I understood you well), that analogy is probably closer.

But, don't take the "factory" metaphor metaphor tootoo far.

The lines implement different algorithms (e.g. use different formulas for the simulation); as. As for the individual car models within each line, suppose the differences are determined by the actual data (state) stored in each instance (the private members effectively serve as parameters to the algorithm encoded by the class)1.

 

1 This detail about how the individual car models are represented is not that relevant to this discussion, except to illustrate that (for the purposes of this example) the hierarchy is organized around lines - i.e., there's a derived class for each line, and not for each model (this keeps the number of subclasses manageable).


So you have a number of parallel hierarchies (generally speaking, parallel hierarchies are not a great idea, avoid them if you can). At the root of each hierarchy is an abstract interface that represent each part; these interfaces let your main game code treat all of the concrete implementations in the same way, without having to worry about the different lines (in other words, the game code can just state what it wants to do in terms of high-level logic, without being obscured by interspersed lines of code that check for different lines in order to produce some special behavior).

The concrete factories that derive from it (ConcreteFactory1, ConcreteFactory2) encapsulate this knowledge for you. In other words, you write the creation logic for a certain car line in one place (in a concrete factory), and then whenever you haveinstantiate it, it gives you a little objectsobject that's kind of a "preset" for constructing car parts for a certain line. So, you'd have a LuxuryCarFactory, SportsCarFactory, EconomyCarFactory, etc.

One final point; you've said:

it is rather a Factory containing different assembly lines (luxury car assembly, sports car line, etc.)

You can infer this from what I've written previously, but it's worth mentioning it explicitly: an abstract factory doesn't contain different assembly lines (inheritance is not a containment relationship). Rather, it is an abstract view (or, in other words, a generalized view) of different assembly lines (concrete factories). The AbstractFactory interface just says "I'm an assembly line, and these are the services that I provide"; it doesn't reveal which assembly line it is. It lets you pick a factory for one line, and then hand it over (as a variable of type AbstractFactory) to other code that can use it to create individual parts, and then use those parts, without ever having to know anything about lines.

So, a variable typed as an AbstractFactory can represent any assembly line, but the instance stored in it is only ever a single assembly line, at any given point in time.

But you're right that the "factory of factories" characterization is not exactly right (it's missleading). Depending on what you mean by "different assembly lines" (and I can't be entirely sure I understood you well), that analogy is probably closer.

But, don't take the "factory" metaphor too far.

The lines implement different algorithms (e.g. use different formulas for the simulation); as for the individual car models within each line, suppose the differences are determined by the actual data (state) stored in each instance (the private members effectively serve as parameters to the algorithm encoded by the class).

So you have a number of parallel hierarchies (generally speaking, parallel hierarchies are not a great idea, avoid them if you can). At the root of each hierarchy is an abstract interface that represent each part; these interfaces let your main game code treat all of the concrete implementations in the same way, without having to worry about the different lines (in other words, the game code can just state what it wants to do in terms of high-level logic, without being obscured by interspersed lines of code that check for different lines in order to produce some special behavior).

The concrete factories that derive from it (ConcreteFactory1, ConcreteFactory2) encapsulate this knowledge for you. In other words, you write the creation logic for a certain car line in one place, and then you have a little objects that's kind of a "preset" for constructing car parts for a certain line. So, you'd have a LuxuryCarFactory, SportsCarFactory, EconomyCarFactory, etc.

But you're right that the "factory of factories" characterization is not exactly right (it's misleading). Depending on what you mean by "different assembly lines" (and I can't be entirely sure I understood you well), that analogy is probably closer.

But, don't take the "factory" metaphor too far.

The lines implement different algorithms (e.g. use different formulas for the simulation). As for the individual car models within each line, suppose the differences are determined by the actual data (state) stored in each instance (the private members effectively serve as parameters to the algorithm encoded by the class)1.

 

1 This detail about how the individual car models are represented is not that relevant to this discussion, except to illustrate that (for the purposes of this example) the hierarchy is organized around lines - i.e., there's a derived class for each line, and not for each model (this keeps the number of subclasses manageable).


So you have a number of parallel hierarchies (generally speaking, parallel hierarchies are not a great idea, avoid them if you can). At the root of each hierarchy is an abstract interface that represent each part; these interfaces let your main game code treat all of the concrete implementations in the same way, without having to worry about the different lines (in other words, the game code can just state what it wants to do in terms of high-level logic, without being obscured by interspersed lines of code that check for different lines in order to produce some special behavior).

The concrete factories that derive from it (ConcreteFactory1, ConcreteFactory2) encapsulate this knowledge for you. In other words, you write the creation logic for a certain car line in one place (in a concrete factory), and then whenever you instantiate it, it gives you a little object that's kind of a "preset" for constructing car parts for a certain line. So, you'd have a LuxuryCarFactory, SportsCarFactory, EconomyCarFactory, etc.

One final point; you've said:

it is rather a Factory containing different assembly lines (luxury car assembly, sports car line, etc.)

You can infer this from what I've written previously, but it's worth mentioning it explicitly: an abstract factory doesn't contain different assembly lines (inheritance is not a containment relationship). Rather, it is an abstract view (or, in other words, a generalized view) of different assembly lines (concrete factories). The AbstractFactory interface just says "I'm an assembly line, and these are the services that I provide"; it doesn't reveal which assembly line it is. It lets you pick a factory for one line, and then hand it over (as a variable of type AbstractFactory) to other code that can use it to create individual parts, and then use those parts, without ever having to know anything about lines.

So, a variable typed as an AbstractFactory can represent any assembly line, but the instance stored in it is only ever a single assembly line, at any given point in time.

Source Link

"which could use the common characteristics of the Car class, but allow each car to have additional properties and methods"

You can already do this without anything else (without introducing a factory, or any other structure).

But you're right that the "factory of factories" characterization is not exactly right (it's missleading). Depending on what you mean by "different assembly lines" (and I can't be entirely sure I understood you well), that analogy is probably closer.

But, don't take the "factory" metaphor too far.

In OOP, a factory is just a glorified constructor; all patterns under the "factory" umbrella are a way to add or enforce some construction-related behavior that cannot be achieved in the usual way, or to add some flexibility to the process (like the ability to pass some kind of a creation-related parameter at one point, but construct the object ("product") at a different point, in some other part of a code). Sometimes we just use them to communicate (a static factory method (or a free function) allows you to give a meaningful name to the creation procedure).

The problem abstract factory tries to solve is the following. Suppose your car object is made of parts; in fact, let's forget about the car object itself, and let's focus on those parts. For simplicity and for illustrative purposes, suppose you're making a racing game, and you want the performance and the "feel" of the car to differ for different models.

A small disclaimer before I continue: I'm not saying that this is the best design or that this is something that you should follow - I'm just setting up a situation where using an abstract factory might make sense.

Suppose you have a unified representation of different car components - different interfaces for things like the engine, suspension, brakes... Maybe you even have some "parts" that are a complete invention by you for the purposes of the simulation: e.g. a MotionController that helps you simulate the actual motion of a car.

But, actually, for each of these, you have concrete classes for each line; e.g. the Engine interface is implemented by LuxuryCarEngine, SportsCarEngine, EconomyCarEngine, etc... You have a LuxuryCarMotionController, SportsCarMotionController, EconomyCarMotionController.

The lines implement different algorithms (e.g. use different formulas for the simulation); as for the individual car models within each line, suppose the differences are determined by the actual data (state) stored in each instance (the private members effectively serve as parameters to the algorithm encoded by the class).

So you have a number of parallel hierarchies (generally speaking, parallel hierarchies are not a great idea, avoid them if you can). At the root of each hierarchy is an abstract interface that represent each part; these interfaces let your main game code treat all of the concrete implementations in the same way, without having to worry about the different lines (in other words, the game code can just state what it wants to do in terms of high-level logic, without being obscured by interspersed lines of code that check for different lines in order to produce some special behavior).

The problem is, you want to make sure that when the player chooses a car model from a different line, you don't mix up the parts from several lines. And even if you can manage to avoid that, you don't want to think about what goes with what every time you need to create something, or worse, repeat that same code in several places in the codebase. That's where the Abstract Factory pattern comes in.

enter image description here

The AbstractFactory interface defines, well, the interface (creation methods and their signatures) that lets you create each part in a way doesn't involve you having to think about what combines with what.

The concrete factories that derive from it (ConcreteFactory1, ConcreteFactory2) encapsulate this knowledge for you. In other words, you write the creation logic for a certain car line in one place, and then you have a little objects that's kind of a "preset" for constructing car parts for a certain line. So, you'd have a LuxuryCarFactory, SportsCarFactory, EconomyCarFactory, etc.

So, whenever your player switches to a model from a different car line (let's say they picked a sports car), you go:

public void SwitchCarModel(CarModel model) {

  // figure out which line they switched to  
  AbstractCarFactory factory;
  switch (model.Line) {
  // ...
  case 'Sports':
      factory = new SportsCarFactory();
      break;
  // ...
  }

  
  // great, now we have an object that knows 
  // which parts to create, and how to create them

  // so, let's use it: 
  this.engine = factory.CreateEngine(model.EngineParameters);
  this.suspension = factory.CreateSuspension(model.SuspensionParameters);
  this.brakes = factory.CreateBrakes(model.BrakeParameters);
  this.motionController = factory.CreateMotionController(
    this.engine, this.suspension, this.brakes, model.Mass, model.RotationalInertia);
  // ...
  // ...
  // etc, etc... (I was making these up on the go) 
}

Hopefully, that clears things up a bit.