Frage

After working through several tutorials and reading various responses on this site, I believe the Abstract Factory pattern would work well for a current project. I am seeking the opinions of those wiser than I to see if I am understanding the pattern correctly.

For my example I'll use a car as the concrete product. I understand there are commonalities in car (drive method, raise and lower windows method, fuel level property) I understand that all models of car can only contain the same characteristics as the abstract product/class/interface (drive, raise/lower windows and read fuel level) and nothing more.

My perception is with the abstract factory pattern, as a factory of factories it would allow me to extend car to LuxuryCar, SportsCar, EconomyCar, MiniVan... which could use the common characteristics of the Car class, but allow each car to have additional properties and methods (SportsCar roll bar property, MiniVan number of cup holders property, LuxuryCar raise/lower large screen TV methods)

So the analogy I'm considering in this case is rather than the Abstract Factory as a factory of factories, it is rather a Factory containing different assembly lines (luxury car assembly, sports car line, etc.)

Does my analogy hold up?

War es hilfreich?

Lösung

"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 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.

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)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 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 (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.

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) 
}

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.

Hopefully, that clears things up a bit.

Andere Tipps

The analogy works. The abstract factory aims to:

Provide an interface for creating families of related or dependent objects without specifying their concrete classes.

It’s not a factory of factories. The output on an abstract factory are not factories, but products that belong to a family of related products.

But since it’s not a single a single kind of products, but several different kind of products, it’s indeed more like having several lines in a factory, which is somewhat suggested by the corresponding class diagram. So your analogy is fine for teaching purpose.

Nevertheless, analogy is not perfect. It might confuse in particular people who have a good understanding of the industrial world: several lines of production are generally producing independent products, e.g. sport cars, SUVs and trucks, and not family of related products: the line for the sports engines and the line for the truck engines would rather be seen as a subpart of the sport line and the truck line.

If you’d really want to come closer to the industrial reality, the abstract factory is the blueprint for a factory, which would build a set of related product parts. Each concrete factory would behave as foreseen by the blueprint but with variations in the parts. You’d have a sport factory for sport engine, sport tires, sport cars, and a different factory for the trucks and truck related items.

Lizenziert unter: CC-BY-SA mit Zuschreibung
scroll top