Related
I need to model a Region that has a contains(point) method. That method determines whether a point falls within the boundaries of the Region.
I currently see two implementations of Region:
One where the region is defined by a start and end postalcode.
One where the region is defined by a lat/lng and a radius.
Now, my main problem is how to define the interface for the contains() method.
Possible solution #1:
One easy solution is to let a point also be defined by a Region:
PostalcodeRegion implements Region
region = new PostalcodeRegion(postalStart: 1000, postalEnd: 2000);
point = new PostalcodeRegion(postalStart: 1234, postalEnd: 1234);
region.contains(point); // true
The interface could look like this:
Region
+ contains(Region region):bool
The problem with this is that the contains() method is not specific, and that we abuse Region to let it be something it is not: a Point.
Possible solution #2:
Or, we define a new point class:
PostalcodeRegion implements Region {}
PostalcodePoint implements Point {}
region = new PostalcodeRegion(postalStart: 1000, postalEnd: 2000);
point = new PostalcodePoint(postalCode: 1234);
region.contains(point); // true
Interface:
Region
+ contains(Point point)
There are several problems with this method:
contains() method is still not specific
There is a pointless Point concept. In and of itself it is/does nothing, it is just a marker interface.
Clarification:
Ok, so this is the first time I encounter where I provide my line of thinking, in the form of possible solutions, that is actually counter productive. My apologies.
Let me try and describe the use case: The system this use case is part of is used to handle insurance claims (amongst other things). When someone claims water damages from a leaking pipe f.e., this system handles the entire workflow from entry by the customer, all the way to sending a repair company etc to close the file.
Now, depending on circumstances, there are two ways to find eligible repair companies: by postal code, or by lat lng.
In the first case (postal code), we could find eligible repair companies with the following code:
region = new PostalCodeRegion(customer.postalCode - 500, customer.postalCode + 500)
region.contains(new PostalCodePoint(repairCompany1.postalCode))
region.contains(new PostalCodePoint(repairCompany2.postalCode))
Or, in the second case:
region = new LatLngRegion(customer.latLng, 50) // 50 km radius
region.contains(new LatLngPoint(repairCompany1.latLng))
region.contains(new LatLngPoint(repairCompany2.latLng))
I want to be able to safely pass around Regions and Points, so I can make sure they are Regions and Points. But I don't actually care about their sub-types.
One thing I would like, but I am not sure it is possible, is to not have to do a runtime check on the passed point in the contains() method. Preferably it would be enforced by contract that I get the correct data (fitting to the chosen Region implementation) to work with.
I'm mostly just thinking out loud. I am inclined to go with method #2, and do a runtime type check of the passed point var in contains() implementation.
I would like to hear some thoughts over one or the other, or even better: a new suggestion I haven't thought of.
It shouldn't be really relevant, but the target platform is PHP. So I can't use generics for example.
Given that a Region would have to operate on two abstractions that have nothing in common (Point and Postcode) then a generic interface is one way of crafting a clean strongly typed common interface, but you should question whether that abstraction is useful to model or not. As developers it's easy to get lost in too much abstractions e.g. maybe a Region<T> is just a Container<T>, etc. and all of a sudden the concepts you work with are nowhere to be found in your domain's Ubiquitous Language.
public interface Region<T> {
public boolean contains(T el);
}
class PostalRegion implements Region<Postcode> {
public boolean contains(Postcode el) { ... }
}
class GeographicRegion implements Region<Point> {
public boolean contains(Point el) { ... }
}
The issue with such question is that it's focusing on how to achieve a specific design rather than explaining the real business problem and that makes it difficult to judge whether or not the solution is appropriate or which alternate solution would.
How would the system leverage the common interface if such interface was implemented? Would it make the model easier to work with?
Since we are forced to assume a problem domain, here's a fictional scenario about developing a city zonage system (I know nothing about this domain so the example may be silly).
In the context of city zonage management, we have uniquely
identified regions that are defined by a postal code range and a
geographical area. We need a system that can answer whether or not a
postal code and/or a point is contained within a specific region.
That gives us a little more context to work with and come up with a model that can fulfill the needs.
We can assume that an application service such as RegionService could maybe look like:
class RegionService {
IRegionRepository regionRepository;
...
boolean regionContainsPoint(regionId, lat, lng) {
region = regionRepository.regionOfId(regionId);
return region.contains(new Point(lat, lng));
}
boolean regionContainsPostcode(regionId, postcode) {
region = regionRepository.regionOfId(regionId);
return region.contains(new Postcode(postcode));
}
}
Then, maybe the design would benefit from applying the Interface Segregation Principle (ISP) where you'd have a Locator<T> interface or explicit PostcodeLocator and PointLocator interfaces, implemented either by Region or other services and used by the RegionService or be a service of their own.
If answering the questions requires complex processing, etc. then maybe the logic should be extracted from a PostalRange and an Area. Applying the ISP would help keeping the design more flexible.
It's important to note that a domain model shines on the write side to protect invariants and compute complex rules & state transitions, but querying needs are often better expressed as stateless services that leverages powerful infrastructure components (e.g. database).
EDIT 1:
I had not realized you mentioned "no generics". Still leaving my answer there b/c I think it still gives good modeling insights and warns about not so useful abstractions. Always think about how the client will be using the API as it helps to determine the usefulness of an abstraction.
EDIT 2 (after clarification added):
It seems that the Specification Pattern could be a useful modeling tool here.
Customers could be said to have a repair company eligibility specification...
E.g.
class Customer {
...
repairCompanyEligibilitySpec() {
//The factory method for creating the specification doesn't have to be on the Customer
postalRange = new PostalRange(this.postalCode - 500, this.postCode + 500);
postalCodeWithin500Range = new PostalCodeWithinRange(postalRange);
locationWithin50kmRadius = new LocationWithinRadius(this.location, Distance.ofKm(50));
return postalCodeWithin500Range.and(locationWithin50kmRadius);
}
}
//Usage
repairCompanyEligibilitySpec = customer.repairCompanyEligibilitySpec();
companyEligibleForRepair = repairCompanyEligibilitySpec.isSatisfiedBy(company);
Note that I haven't really understood what you mean by "I want to be able to safely pass around Regions and Points" or at least failed to understand why this requires a common interface so perhaps the proposed design wouldn't be suitable. Making the policy/rule/spec explicit has several advantages and the specification pattern is easily extensible to support features such as describing why a company was not eligible, etc.
e.g.
unsatisfiedSpec = repairCompanyEligibilitySpec.remainderUnsatisfiedBy(company);
reasonUnsatisfied = unsatisfiedSpec.describe();
Ultimately the specification itself doesn't have to implement the operations. You could use the Visitor Pattern in order to add new operations to a set of specifications and/or to segregate operations by logical layer.
I think it's better not to have the implementation of contains in the data object and to create a seperate class for each of the contains implementations. Something like:
class Region
{
...
}
class RegionCheckManager
{
function registerCheckerForPointType(string $pointType, RegionCheckerInterface $checkerImplementation): void
{
...
}
function contains(PointInterface $point, Region $region): bool
{
return $this->getCheckerForPoint($point)->check($region, $point);
}
/** get correct checker for point type **/
private function getCheckerForPoint(PointInterface $point): RegionCheckerInterface
{
...
}
}
interface RegionCheckerInterface
{
public function contains(PointInterface $point): bool;
}
class PostcodeChecker implements RegionCheckerInterface
{
...
}
class PointChecker implements RegionCheckerInterface
{
...
}
Postcode and Point are different conceptual things, they are two different types. Postcode is a scalar value, Point is a geographic item. In fact, your PostalCodeRegion class is a range of scalar value, your LatLngRegion class is a geographic area that has center coordinates and radius. You try to combine two incompatible abstractions. Attempt to make one interface for two absolutely different things is the wrong way which leads to unobvious code and implicit abstractions. You should rethink your abstractions. For example:
What is a postcode? It is a positive number in the simplest case. You can create a Postcode class as a value object and implement simple methods to work with its data.
class Postcode
{
private $number;
public function __constuct(int $number)
{
assert($value <= 0, 'Postcode must be greater than 0');
$this->number = $number;
}
public function getNumber(): int
{
return $this->number;
}
public function greatOrEqual(Postalcode $value): bool
{
return $this->number >= $value->getNumber();
}
public function lessOrEqual(Postalcode $value): bool
{
return $this->number <= $value->getNumber();
}
}
What is a postcode range? It a set of postcodes that contains start postcode and end postcode. So you can also create a value object of a range and implement contains method in it.
class PostcodeRange
{
private $start;
private $end;
public function __construct(Postcode $start, Postcode $end)
{
assert(!$start->lessOrEqual($end));
$this->start = $start;
$this->end = $end;
}
public function contains(Postcode $value): bool
{
return $value->greatOrEqual($this->start) && $value->lessOrEqual($this->end);
}
}
What is a point? It is a geographic item that has some coordinates.
class Point
{
private $lan;
private $lng;
public function __constuct(float $lan, float $lng)
{
$this->lan = $lan;
$this->lng = $lng;
}
public function getLan(): float
{
return $this->lan;
}
public function getLng(): float
{
return $this->lng;
}
}
What is an area? It is a geographic region that has some borders. In your case, those borders defined with a circle that has a center point and some radius.
class Area
{
private $center;
private $radius;
public function __constuct(Point $center, int $radius)
{
$this->center = $center;
$this->radius = $radius;
}
public function contains(Point $point): bool
{
// implementation of method
}
}
So, each company has a postcode and some location defined by its coordinates.
class Company
{
private $postcode;
private $location;
public function __construct(Postcode $postcode, Point $location)
{
$this->postcode = $postcode;
$this->location = $location;
}
public function getPostcode(): Postcode
{
return $this->postcode;
}
public function getLocation(): Point
{
return $this->location;
}
}
So, how you said you have a list of companies and try to find it by postcode range or area. So you can create company collection which can contain all companies and can implement algorithms to search by necessary criteria.
class CompanyCollection
{
private $items;
public function __constuct(array $items)
{
$this->items = $items;
}
public function findByPostcodeRange(PostcodeRange $range): CompanyCollection
{
$items = array_filter($this->items, function(Company $item) use ($range) {
return $range->contains($item->getPostcode());
});
return new static($items);
}
public function findByArea(Area $area): CompanyCollection
{
$items = array_filter($this->items, function(Company $item) use ($area) {
return $area->contains($item->getLocation());
});
return new static($items);
}
}
Example of usage:
$collection = new CompanyCollection([
new Company(new Postcode(1200), new Point(1, 1)),
new Company(new Postcode(1201), new Point(2, 2)),
])
$range = new PostcodeRange(new Postcode(1000), new Postcode(2000));
$area = new Area(new Point(0, 0), 50);
// find only by postcode range
$collection->findByPostcodeRange($range);
// find only by area
$collection->findByArea($area);
// find by postcode range and area
$collection->findByPostcodeRange($range)->findByArea($area);
If I understand the problem correctly, you have some module M, which needs to accept some 3 objects:
implementation of region (postcodes vs radius, let's call them R1 vs R2)
implementation of point (postcode vs lat/lng, P1 vs P2)
some API C to check the point is within the region
and it then applies the 3rd object on the first 2.
(Could be that C is R1 or R2, that's immaterial for the problem definition).
So spelling out the problem: you can apply C on R1+P1 or R2+P2, but not R1+P2 or R2+P1.
I'm afraid the only way to implement it in a type-safe manner is as follows:
C is an interface, apply().
C1 implements C, and has fields of type R1, P1.
C2 implements C, and has fields of type R2, P2.
The caller builds either C1 or C2, passes it to M, and M calls c.apply().
Note how M doesn't even see points, only the checker interface C. That's because there is nothing common between P1 and P2 that anyone other than then C can use.
I'm trying to create a basic Pacman game in C++ (I'll use Java syntax in this question as this is somewhat easier to demonstrate), but I can't find a good design option.
So far I have 4 classes:
- Monster: Can be subclassed for monster-specific behaviour and contains all logic for the monsters
- Player: Contains player-logic
- Map: Contains a 2d array representing the map. This array specifies which positions are walls or Pacman food
- Game: contains a Player, a Map and a list of Monsters.
To keep it simple:
public class Game {
Player player;
Map map;
ArrayList<Monster> monsters;
public Game() {
player = new Player();
map = new Map();
monsters = new ArrayList<Monster();
monsters.add(new ScaryMonster());
monsters.add(new DumpMonster());
}
public void update() {
player.update();
map.update();
for (Monster monster: monsters) {
monster.update();
}
public void draw() {
map.draw();
player.draw();
for (Monster monster: monsters) {
monster.draw();
}
}
So all I have to do now is to create a Game object and call update() and draw() on it every time. Very simple. But it doesn't work.
Assume I call update() on the player-object and the player (which is the Pacman ofcourse) hits food. In that case, the map-object should get notified of this (and the position) to remove the food from the 2d-array. Assume the player kills a monster, the position of the monster should get changed (the Monster class has a "position field"). And you can imagine a lot more of these situations.
An option would be to pass the map and monster object as parameters in the update() and draw() method of the player object. And to pass the player and monster objects as parameters in the method calls of map. But that surely doesn't sound like a good OO design.
What's a good OO way to solve this? I was thinking about using the Observer pattern (so Game is the subject, player, map and monsters are observers), but that doesn't make any sense: that way the observers will have to let the subject know of any changes, which is obviously not the correct way of using this.
Any tips would be very welcome.
Thank you very much :)
Why don't you try mapping actions?
Every action has a reaction. So let's say the pacman hits food. That's an action, "hitting food", which in turn has a reaction (notifying the map, or the food, or whatever you like) that the food is not there any longer.
Now imagine the pacman hits a monster, that's another action... what would be the reaction to that? Well it might cause the monster to get dead (a call to the BeDeath method :P) or it could cause the pacman to get death... whatever it is it allow you to chain actions to reactions in the game.
That means the logic of the game, the rules would be in the game class, who in addition already knows all the elements needed and can communicate with each one.
Edit: A simple example (very simple, as the game gets more complex you'll need to think better about actions and reactions structure)
public void IGameInfo
{
List<Monster> Monsters {get;}
Pacman Pacman {get;}
Map Map {get;}
}
public void ComputeReactions()
{
foreach (actionChecker in Actions)
{
actionChecker.Check(gameInfo);
}
}
public void ComputeDotEaten(IGameInfo gameInfo)
{
foreach (dot in gameInfo.Map.Dots)
if (pacman.location == dot.location)
dot.MarkEaten();
}
public void ComputeMonsterEaten(IGameInfo gameInfo)
{
foreach (Monster in gameInfo.monsters)
if (gameInfo.pacman.location == gameInfo.monster.location &&
gameInfo.pacman.Invulnerable)
monster.MarkDeath();
else
Game.EndGame();
}
Or if you like you could also map the reactions
public void ComputeDotEaten(IGameInfo gameInfo)
{
foreach (dot in gameInfo.Map.Dots)
if (pacman.location == dot.location)
Reactions["DotEaten"].Execute(dot);
}
Note that for that to work all you reactions must share a common signature (i.e, taking an array of objects that are cast to the expected parameters)
I am developing a game, the game have different mode. Easy, Normal, and Difficult.
So, I'm thinking about how to store the game mode. My first idea is using number to represent the difficulty.
Easy = 0 Normal = 1 Difficult = 2
So, my code will have something like this:
switch(gameMode){
case 0:
//easy
break;
case 1:
//normal
break;
case 3:
//difficult
break;
}
But I think it have some problems, if I add a new mode, for example, "Extreme", I need to add case 4... ... it seems not a gd design.
So, I am thinking making a gameMode object, and different gameMode is sub class of the super class gameMode.
The gameMode object is something like this:
class GameMode{
int maxEnemyNumber;
int maxWeaponNumber;
public static GameMode init(){
GameMode gm = GameMode();
gm.maxEnemyNumber = 0;
gm.maxWeaponNumber = 0;
return gm;
}
}
class EasyMode extends GameMode{
public static GameMode init(){
GameMode gm = super.init();
gm.maxEnemyNumber = 10;
gm.maxWeaponNumber = 100;
return gm;
}
}
class NormalMode extends GameMode{
public static GameMode init(){
GameMode gm = super.init();
gm.maxEnemyNumber = 20;
gm.maxWeaponNumber = 80;
return gm;
}
}
But I think it seems too "bulky" to create an object to store gameMode, my "gameMode" only store different variables for game settings.... Is that any simple way to store data only instead of making an Object? thz u.
I think you are trying to represent a table of configuration data. Either put this in a configuration file if you're using a language that supports that, or use literal data in your code.
For instance, you might write this in C:
typedef enum difficulties {
DIFFICULTY_EASY,
DIFFICULTY_MEDIUM,
DIFFICULTY_HARD
} difficulties;
struct {
int max_enemies;
int max_weapons;
} difficulty_settings[] = {
{10, 4},
{20, 5},
{30, 6}
};
And when you want to read a particular setting, for example max_enemies for the easy level, then you can writedifficulty_settings[DIFFICULTY_EASY].max_enemies
It's easy to add more configuration (either more parameters, or more difficulty levels) by extending the table.
The overriding goal you should have here is to centralize the logic for retrieving the values related to different levels. By providing one place where these values are stored, you minimize the number of places within the code you need to change if you add another level, add other values, etc.
A class interface is a good choice for this solution. However, if you have a limited number of configuration options represented by the class, there is no reason you need to use inheritance. You can start out with a single class that encapsulates the logic. If the rest of your code retrieves its settings via the class interface you can later introduce a more complex design, such as subclasses for each mode, if it becomes necessary with limited modifications to the rest of your game.
For example, a first implementation may be something like
enum mode {
MODE_EASY = 0,
MODE_NORMAL = 1,
MODE_DIFFICULT = 2,
};
class gameSettings {
public gameSettings(mode GameMode) {
m_mode = GameMode;
}
public int getMaxWeaponNumber() {
int maxWeaponNumber;
switch(m_mode) {
case EASY_MODE:
maxWeaponNumber = 100;
break;
// Other mode settings.
}
return maxWeaponNumber;
}
// Other game settings....
private mode m_mode;
}
This combines the straightforwardness of a switch() statement with the benefits of a class interface. You can also swap out your switch() statement with a lookup table, as suggested by another poster, or some other mechanism as appropriate for your application.
I don't know java (which is what your examples look like), so I present my ideas in some simple C#.
Here is an idea. Use your game mode as a flag instead. If you start with:
[Flags]
enum GameModes
{
Unknown = 0,
ModeA = 1,
ModeB = 2,
ModeC = 4,
}
Now you have levels 1-7 available.
GameModes Difficulty = GameModes.ModeA | GameModes.ModeB; // difficulty = 3
GameModes Difficulty = GameModes.ModeB; // difficulty = 2
In addition, either method you showed will require you to add more options should levels (modes) change, get added, etc. Have your mode templates read in from XML (or other source of your choice), save the mode data into a serializable class. I don't think you should need base class extended by anything.
Use the switch approach in the constructor of your GameMode class.
Besides some syntax issues, I think you're on the right track. I don't think you have to worry about memory, considering there is probably only one mode at once. This is a form of the strategy pattern. You could extend it so the modes do more. For instance, instead of basically just holding constants, perhaps there could be a generateEnemies method that actually creates a set or list of enemies. This moves more of the strategy into the mode object. Sane defaults in the superclass can help avoid redundant code.
Its difficult to say what kind of refactoring could be done here, as there is too less information about other classes. But you could check the State pattern which encapsulates different behaviours in different state objects. Your approach of extending a base GameMode class is very similar to the state pattern. I think it's better than a switch-case-block... and patterns are reliable ways of doing things, if well applied.
Why do you think the switch is harder to mantain? If you add another mode you will have to add code, no matter what solution you employ.
The only case I can think of where you don't have to add code if you add another mode is if you generate the parameters of the game from the value of gameMode.
For instance: maxenemy = 5 * gameMode;
I think that unless you have very complicated initialisation to perform a switch is more than sufficient. I know, I know, objects and classes are nice and all that jazz, but if you just have to define a few vars and the thing works, investing time in developing a complex game mode class may not be a rewarding solution after all (I mean, how many game modes are you planning to add?).
Make use of the strategy pattern.
In Java terms:
public interface Strategy {
void execute();
}
public class SomeStrategy implements Strategy {
public void execute() {
System.out.println("Some logic.");
}
}
which you use as follows:
Map<String, Strategy> strategies = new HashMap<String, Strategy>();
strategies.put("strategyName1", new SomeStrategy1());
strategies.put("strategyName2", new SomeStrategy2());
strategies.put("strategyName3", new SomeStrategy3());
// ...
strategies.get(s).execute();
I am new to OOP. Though I understand what polymorphism is, but I can't get the real use of it. I can have functions with different name. Why should I try to implement polymorphism in my application.
Classic answer: Imagine a base class Shape. It exposes a GetArea method. Imagine a Square class and a Rectangle class, and a Circle class. Instead of creating separate GetSquareArea, GetRectangleArea and GetCircleArea methods, you get to implement just one method in each of the derived classes. You don't have to know which exact subclass of Shape you use, you just call GetArea and you get your result, independent of which concrete type is it.
Have a look at this code:
#include <iostream>
using namespace std;
class Shape
{
public:
virtual float GetArea() = 0;
};
class Rectangle : public Shape
{
public:
Rectangle(float a) { this->a = a; }
float GetArea() { return a * a; }
private:
float a;
};
class Circle : public Shape
{
public:
Circle(float r) { this->r = r; }
float GetArea() { return 3.14f * r * r; }
private:
float r;
};
int main()
{
Shape *a = new Circle(1.0f);
Shape *b = new Rectangle(1.0f);
cout << a->GetArea() << endl;
cout << b->GetArea() << endl;
}
An important thing to notice here is - you don't have to know the exact type of the class you're using, just the base type, and you will get the right result. This is very useful in more complex systems as well.
Have fun learning!
Have you ever added two integers with +, and then later added an integer to a floating-point number with +?
Have you ever logged x.toString() to help you debug something?
I think you probably already appreciate polymorphism, just without knowing the name.
In a strictly typed language, polymorphism is important in order to have a list/collection/array of objects of different types. This is because lists/arrays are themselves typed to contain only objects of the correct type.
Imagine for example we have the following:
// the following is pseudocode M'kay:
class apple;
class banana;
class kitchenKnife;
apple foo;
banana bar;
kitchenKnife bat;
apple *shoppingList = [foo, bar, bat]; // this is illegal because bar and bat is
// not of type apple.
To solve this:
class groceries;
class apple inherits groceries;
class banana inherits groceries;
class kitchenKnife inherits groceries;
apple foo;
banana bar;
kitchenKnife bat;
groceries *shoppingList = [foo, bar, bat]; // this is OK
Also it makes processing the list of items more straightforward. Say for example all groceries implements the method price(), processing this is easy:
int total = 0;
foreach (item in shoppingList) {
total += item.price();
}
These two features are the core of what polymorphism does.
Advantage of polymorphism is client code doesn't need to care about the actual implementation of a method.
Take look at the following example.
Here CarBuilder doesn't know anything about ProduceCar().Once it is given a list of cars (CarsToProduceList) it will produce all the necessary cars accordingly.
class CarBase
{
public virtual void ProduceCar()
{
Console.WriteLine("don't know how to produce");
}
}
class CarToyota : CarBase
{
public override void ProduceCar()
{
Console.WriteLine("Producing Toyota Car ");
}
}
class CarBmw : CarBase
{
public override void ProduceCar()
{
Console.WriteLine("Producing Bmw Car");
}
}
class CarUnknown : CarBase { }
class CarBuilder
{
public List<CarBase> CarsToProduceList { get; set; }
public void ProduceCars()
{
if (null != CarsToProduceList)
{
foreach (CarBase car in CarsToProduceList)
{
car.ProduceCar();// doesn't know how to produce
}
}
}
}
class Program
{
static void Main(string[] args)
{
CarBuilder carbuilder = new CarBuilder();
carbuilder.CarsToProduceList = new List<CarBase>() { new CarBmw(), new CarToyota(), new CarUnknown() };
carbuilder.ProduceCars();
}
}
Polymorphism is the foundation of Object Oriented Programming. It means that one object can be have as another project. So how does on object can become other, its possible through following
Inheritance
Overriding/Implementing parent Class behavior
Runtime Object binding
One of the main advantage of it is switch implementations. Lets say you are coding an application which needs to talk to a database. And you happen to define a class which does this database operation for you and its expected to do certain operations such as Add, Delete, Modify. You know that database can be implemented in many ways, it could be talking to file system or a RDBM server such as MySQL etc. So you as programmer, would define an interface that you could use, such as...
public interface DBOperation {
public void addEmployee(Employee newEmployee);
public void modifyEmployee(int id, Employee newInfo);
public void deleteEmployee(int id);
}
Now you may have multiple implementations, lets say we have one for RDBMS and other for direct file-system
public class DBOperation_RDBMS implements DBOperation
// implements DBOperation above stating that you intend to implement all
// methods in DBOperation
public void addEmployee(Employee newEmployee) {
// here I would get JDBC (Java's Interface to RDBMS) handle
// add an entry into database table.
}
public void modifyEmployee(int id, Employee newInfo) {
// here I use JDBC handle to modify employee, and id to index to employee
}
public void deleteEmployee(int id) {
// here I would use JDBC handle to delete an entry
}
}
Lets have File System database implementation
public class DBOperation_FileSystem implements DBOperation
public void addEmployee(Employee newEmployee) {
// here I would Create a file and add a Employee record in to it
}
public void modifyEmployee(int id, Employee newInfo) {
// here I would open file, search for record and change values
}
public void deleteEmployee(int id) {
// here I search entry by id, and delete the record
}
}
Lets see how main can switch between the two
public class Main {
public static void main(String[] args) throws Exception {
Employee emp = new Employee();
... set employee information
DBOperation dboper = null;
// declare your db operation object, not there is no instance
// associated with it
if(args[0].equals("use_rdbms")) {
dboper = new DBOperation_RDBMS();
// here conditionally, i.e when first argument to program is
// use_rdbms, we instantiate RDBM implementation and associate
// with variable dboper, which delcared as DBOperation.
// this is where runtime binding of polymorphism kicks in
// JVM is allowing this assignment because DBOperation_RDBMS
// has a "is a" relationship with DBOperation.
} else if(args[0].equals("use_fs")) {
dboper = new DBOperation_FileSystem();
// similarly here conditionally we assign a different instance.
} else {
throw new RuntimeException("Dont know which implemnation to use");
}
dboper.addEmployee(emp);
// now dboper is refering to one of the implementation
// based on the if conditions above
// by this point JVM knows dboper variable is associated with
// 'a' implemenation, and it will call appropriate method
}
}
You can use polymorphism concept in many places, one praticle example would be: lets you are writing image decorer, and you need to support the whole bunch of images such as jpg, tif, png etc. So your application will define an interface and work on it directly. And you would have some runtime binding of various implementations for each of jpg, tif, pgn etc.
One other important use is, if you are using java, most of the time you would work on List interface, so that you can use ArrayList today or some other interface as your application grows or its needs change.
Polymorphism allows you to write code that uses objects. You can then later create new classes that your existing code can use with no modification.
For example, suppose you have a function Lib2Groc(vehicle) that directs a vehicle from the library to the grocery store. It needs to tell vehicles to turn left, so it can call TurnLeft() on the vehicle object among other things. Then if someone later invents a new vehicle, like a hovercraft, it can be used by Lib2Groc with no modification.
I guess sometimes objects are dynamically called. You are not sure whether the object would be a triangle, square etc in a classic shape poly. example.
So, to leave all such things behind, we just call the function of derived class and assume the one of the dynamic class will be called.
You wouldn't care if its a sqaure, triangle or rectangle. You just care about the area. Hence the getArea method will be called depending upon the dynamic object passed.
One of the most significant benefit that you get from polymorphic operations is ability to expand.
You can use same operations and not changing existing interfaces and implementations only because you faced necessity for some new stuff.
All that we want from polymorphism - is simplify our design decision and make our design more extensible and elegant.
You should also draw attention to Open-Closed Principle (http://en.wikipedia.org/wiki/Open/closed_principle) and for SOLID (http://en.wikipedia.org/wiki/Solid_%28Object_Oriented_Design%29) that can help you to understand key OO principles.
P.S. I think you are talking about "Dynamic polymorphism" (http://en.wikipedia.org/wiki/Dynamic_polymorphism), because there are such thing like "Static polymorphism" (http://en.wikipedia.org/wiki/Template_metaprogramming#Static_polymorphism).
You don't need polymorphism.
Until you do.
Then its friggen awesome.
Simple answer that you'll deal with lots of times:
Somebody needs to go through a collection of stuff. Let's say they ask for a collection of type MySpecializedCollectionOfAwesome. But you've been dealing with your instances of Awesome as List. So, now, you're going to have to create an instance of MSCOA and fill it with every instance of Awesome you have in your List<T>. Big pain in the butt, right?
Well, if they asked for an IEnumerable<Awesome>, you could hand them one of MANY collections of Awesome. You could hand them an array (Awesome[]) or a List (List<Awesome>) or an observable collection of Awesome or ANYTHING ELSE you keep your Awesome in that implements IEnumerable<T>.
The power of polymorphism lets you be type safe, yet be flexible enough that you can use an instance many many different ways without creating tons of code that specifically handles this type or that type.
Tabbed Applications
A good application to me is generic buttons (for all tabs) within a tabbed-application - even the browser we are using it is implementing Polymorphism as it doesn't know the tab we are using at the compile-time (within the code in other words). Its always determined at the Run-time (right now! when we are using the browser.)
I am looking for an alternative to the visitor pattern. Let me just focus on a couple of pertinent aspects of the pattern, while skipping over unimportant details. I'll use a Shape example (sorry!):
You have a hierarchy of objects that implement the IShape interface
You have a number of global operations that are to be performed on all objects in the hierarchy, e.g. Draw, WriteToXml etc...
It is tempting to dive straight in and add a Draw() and WriteToXml() method to the IShape interface. This is not necessarily a good thing - whenever you wish to add a new operation that is to be performed on all shapes, each IShape-derived class must be changed
Implementing a visitor for each operation i.e. a Draw visitor or a WirteToXml visitor encapsulates all the code for that operation in one class. Adding a new operation is then a matter of creating a new visitor class that performs the operation on all types of IShape
When you need to add a new IShape-derived class, you essentially have the same problem as you did in 3 - all visitor classes must be changed to add a method to handle the new IShape-derived type
Most places where you read about the visitor pattern state that point 5 is pretty much the main criteria for the pattern to work and I totally agree. If the number of IShape-derived classes is fixed, then this can be a quite elegant approach.
So, the problem is when a new IShape-derived class is added - each visitor implementation needs to add a new method to handle that class. This is, at best, unpleasant and, at worst, not possible and shows that this pattern is not really designed to cope with such changes.
So, the question is has anybody come across alterative approaches to handling this situation?
You might want to have a look at the Strategy pattern. This still gives you a separation of concerns while still being able to add new functionality without having to change each class in your hierarchy.
class AbstractShape
{
IXmlWriter _xmlWriter = null;
IShapeDrawer _shapeDrawer = null;
public AbstractShape(IXmlWriter xmlWriter,
IShapeDrawer drawer)
{
_xmlWriter = xmlWriter;
_shapeDrawer = drawer;
}
//...
public void WriteToXml(IStream stream)
{
_xmlWriter.Write(this, stream);
}
public void Draw()
{
_drawer.Draw(this);
}
// any operation could easily be injected and executed
// on this object at run-time
public void Execute(IGeneralStrategy generalOperation)
{
generalOperation.Execute(this);
}
}
More information is in this related discussion:
Should an object write itself out to a file, or should another object act on it to perform I/O?
There is the "Visitor Pattern With Default", in which you do the visitor pattern as normal but then define an abstract class that implements your IShapeVisitor class by delegating everything to an abstract method with the signature visitDefault(IShape).
Then, when you define a visitor, extend this abstract class instead of implementing the interface directly. You can override the visit* methods you know about at that time, and provide for a sensible default. However, if there really isn't any way to figure out sensible default behavior ahead of time, you should just implement the interface directly.
When you add a new IShape subclass, then, you fix the abstract class to delegate to its visitDefault method, and every visitor that specified a default behavior gets that behavior for the new IShape.
A variation on this if your IShape classes fall naturally into a hierarchy is to make the abstract class delegate through several different methods; for example, an DefaultAnimalVisitor might do:
public abstract class DefaultAnimalVisitor implements IAnimalVisitor {
// The concrete animal classes we have so far: Lion, Tiger, Bear, Snake
public void visitLion(Lion l) { visitFeline(l); }
public void visitTiger(Tiger t) { visitFeline(t); }
public void visitBear(Bear b) { visitMammal(b); }
public void visitSnake(Snake s) { visitDefault(s); }
// Up the class hierarchy
public void visitFeline(Feline f) { visitMammal(f); }
public void visitMammal(Mammal m) { visitDefault(m); }
public abstract void visitDefault(Animal a);
}
This lets you define visitors that specify their behavior at whatever level of specificity you wish.
Unfortunately, there is no way to avoid doing something to specify how visitors will behave with a new class - either you can set up a default ahead of time, or you can't. (See also the second panel of this cartoon )
I maintain a CAD/CAM software for metal cutting machine. So I have some experience with this issues.
When we first converted our software (it was first released in 1985!) to a object oriented designed I did just what you don't like. Objects and Interfaces had Draw, WriteToFile, etc. Discovering and reading about Design Patterns midway through the conversion helped a lot but there were still a lot of bad code smells.
Eventually I realized that none of these types of operations were really the concern of the object. But rather the various subsystems that needed to do the various operations. I handled this by using what is now called a Passive View Command object, and well defined Interface between the layers of software.
Our software is structured basically like this
The Forms implementing various Form
Interface. These forms are a thing shell passing events to the UI Layer.
UI layer that receives Events and manipulate forms through the Form interface.
The UI Layer will execute commands that all implement the Command interface
The UI Object have interfaces of their own that the command can interact with.
The Commands get the information they need, process it, manipulates the model and then report back to the UI Objects which then does anything needed with the forms.
Finally the models which contains the various objects of our system. Like Shape Programs, Cutting Paths, Cutting Table, and Metal Sheets.
So Drawing is handled in the UI Layer. We have different software for different machines. So while all of our software share the same model and reuse many of the same commands. They handle things like drawing very different. For a example a cutting table is draw different for a router machine versus a machine using a plasma torch despite them both being esstentially a giant X-Y flat table. This because like cars the two machines are built differently enough so that there is a visual difference to the customer.
As for shapes what we do is as follows
We have shape programs that produce cutting paths through the entered parameters. The cutting path knows which shape program produced. However a cutting path isn't a shape. It just the information needed to draw on the screen and to cut the shape. One reason for this design is that cutting paths can be created without a shape program when they are imported from a external app.
This design allows us to separate the design of the cutting path from the design of the shape which are not always the same thing. In your case likely all you need to package is the information needed to draw the shape.
Each shape program has a number of views implementing a IShapeView Interface. Through the IShapeView interface the shape program can tell the generic shape form we have how to setup itself up to show the parameters of that shape. The generic shape form implements a IShapeForm interface and registers itself with the ShapeScreen Object. The ShapeScreen Object registers itself with our application object. The shape views use whatever shapescreen that registers itself with the application.
The reason for the multiple views that we have customers that like to enter shapes in different ways. Our customer base is split in half between those who like to enter shape parameters in a table form and those who like to enter with a graphical representation of the shape in front of them. We also need to access the parameters at times through a minimal dialog rather than our full shape entry screen. Hence the multiple views.
Commands that manipulate shapes fall in one of two catagories. Either they manipulate the cutting path or they manipulate the shape parameters. To manipulate the shape parameters generally we either throw them back into the shape entry screen or show the minimal dialog. Recalculate the shape, and display it in the same location.
For the cutting path we bundled up each operation in a separate command object. For example we have command objects
ResizePath
RotatePath
MovePath
SplitPath
and so on.
When we need to add new functionality we add another command object, find a menu, keyboard short or toolbar button slot in the right UI screen and setup the UI object to ececute that command.
For example
CuttingTableScreen.KeyRoute.Add vbShift+vbKeyF1, New MirrorPath
or
CuttingTableScreen.Toolbar("Edit Path").AddButton Application.Icons("MirrorPath"),"Mirror Path", New MirrorPath
In both instances the Command object MirrorPath is being associated with a desired UI element. In the execute method of MirrorPath is all the code needed to mirror the path in a particular axis. Likely the command will have it's own dialog or use one of the UI elements to ask the user which axis to mirror. None of this is making a visitor, or adding a method to the path.
You will find that a lot can be handled through bundling actions into commands. However I caution that is not a black or white situation. You will still find that certain things work better as methods on the original object. In may experience I found that perhaps 80% of what I used to do in methods were able to be moved into the command. The last 20% just plain work better on the object.
Now some may not like this because it seems to violate encapsulations. From maintaining our software as a object oriented system for the last decade I have to say the MOST important long term thing you can do is clearly document the interactions between the different layers of your software and between the different objects.
Bundling actions into Command objects helps with this goal way better than a slavish devotion to the ideals of encapsulation. Everything that is needs to be done to Mirror a Path is bundled in the Mirror Path Command Object.
Visitor design pattern is a workaround, not a solution to the problem. Short answer would be pattern matching.
Regardless of what path you take, the implementation of alternate functionality that is currently provided by the Visitor pattern will have to 'know' something about the concrete implementation of the interface that it is working on. So there is no getting around the fact that you are going to have to write addition 'visitor' functionality for each additional implementation. That said what you are looking for is a more flexible and structured approach to creating this functionality.
You need to separate out the visitor functionality from the interface of the shape.
What I would propose is a creationist approach via an abstract factory to create replacement implementations for visitor functionality.
public interface IShape {
// .. common shape interfaces
}
//
// This is an interface of a factory product that performs 'work' on the shape.
//
public interface IShapeWorker {
void process(IShape shape);
}
//
// This is the abstract factory that caters for all implementations of
// shape.
//
public interface IShapeWorkerFactory {
IShapeWorker build(IShape shape);
...
}
//
// In order to assemble a correct worker we need to create
// and implementation of the factory that links the Class of
// shape to an IShapeWorker implementation.
// To do this we implement an abstract class that implements IShapeWorkerFactory
//
public AbsractWorkerFactory implements IShapeWorkerFactory {
protected Hashtable map_ = null;
protected AbstractWorkerFactory() {
map_ = new Hashtable();
CreateWorkerMappings();
}
protected void AddMapping(Class c, IShapeWorker worker) {
map_.put(c, worker);
}
//
// Implement this method to add IShape implementations to IShapeWorker
// implementations.
//
protected abstract void CreateWorkerMappings();
public IShapeWorker build(IShape shape) {
return (IShapeWorker)map_.get(shape.getClass())
}
}
//
// An implementation that draws circles on graphics
//
public GraphicsCircleWorker implements IShapeWorker {
Graphics graphics_ = null;
public GraphicsCircleWorker(Graphics g) {
graphics_ = g;
}
public void process(IShape s) {
Circle circle = (Circle)s;
if( circle != null) {
// do something with it.
graphics_.doSomething();
}
}
}
//
// To replace the previous graphics visitor you create
// a GraphicsWorkderFactory that implements AbstractShapeFactory
// Adding mappings for those implementations of IShape that you are interested in.
//
public class GraphicsWorkerFactory implements AbstractShapeFactory {
Graphics graphics_ = null;
public GraphicsWorkerFactory(Graphics g) {
graphics_ = g;
}
protected void CreateWorkerMappings() {
AddMapping(Circle.class, new GraphicCircleWorker(graphics_));
}
}
//
// Now in your code you could do the following.
//
IShapeWorkerFactory factory = SelectAppropriateFactory();
//
// for each IShape in the heirarchy
//
for(IShape shape : shapeTreeFlattened) {
IShapeWorker worker = factory.build(shape);
if(worker != null)
worker.process(shape);
}
It still means that you have to write concrete implementations to work on new versions of 'shape' but because it is completely separated from the interface of shape, you can retrofit this solution without breaking the original interface and software that interacts with it. It acts as a sort of scaffolding around the implementations of IShape.
If you're using Java: Yes, it's called instanceof. People are overly scared to use it. Compared to the visitor pattern, it's generally faster, more straightforward, and not plagued by point #5.
If you have n IShapes and m operations that behave differently for each shape, then you require n*m individual functions. Putting these all in the same class seems like a terrible idea to me, giving you some sort of God object. So they should be grouped either by IShape, by putting m functions, one for each operation, in the IShape interface, or grouped by operation (by using the visitor pattern), by putting n functions, one for each IShape in each operation/visitor class.
You either have to update multiple classes when you add a new IShape or when you add a new operation, there is no way around it.
If you are looking for each operation to implement a default IShape function, then that would solve your problem, as in Daniel Martin's answer: https://stackoverflow.com/a/986034/1969638, although I would probably use overloading:
interface IVisitor
{
void visit(IShape shape);
void visit(Rectangle shape);
void visit(Circle shape);
}
interface IShape
{
//...
void accept(IVisitor visitor);
}
I have actually solved this problem using the following pattern. I do not know if it has a name or not!
public interface IShape
{
}
public interface ICircleShape : IShape
{
}
public interface ILineShape : IShape
{
}
public interface IShapeDrawer
{
void Draw(IShape shape);
/// <summary>
/// Returns the type of the shape this drawer is able to draw!
/// </summary>
Type SourceType { get; }
}
public sealed class LineShapeDrawer : IShapeDrawer
{
public Type SourceType => typeof(ILineShape);
public void Draw(IShape drawing)
{
if (drawing is ILineShape)
{
// Code to draw the line
}
}
}
public sealed class CircleShapeDrawer : IShapeDrawer
{
public Type SourceType => typeof(ICircleShape);
public void Draw(IShape drawing)
{
if (drawing is ICircleShape)
{
// Code to draw the circle
}
}
}
public sealed class ShapeDrawingClient
{
private readonly IDictionary<Type, IShapeDrawer> m_shapeDrawers =
new Dictionary<Type, IShapeDrawer>();
public void Add(IShapeDrawer shapeDrawer)
{
m_shapeDrawers[shapeDrawer.SourceType] = shapeDrawer;
}
public void Draw(IShape shape)
{
Type[] interfaces = shape.GetType().GetInterfaces();
foreach (Type #interface in interfaces)
{
if (m_shapeDrawers.TryGetValue(#interface, out IShapeDrawer drawer))
{
drawer.Draw(drawing);
return;
}
}
}
}
Usage:
LineShapeDrawer lineShapeDrawer = new LineShapeDrawer();
CircleShapeDrawer circleShapeDrawer = new CircleShapeDrawer();
ShapeDrawingClient client = new ShapeDrawingClient ();
client.Add(lineShapeDrawer);
client.Add(circleShapeDrawer);
foreach (IShape shape in shapes)
{
client.Draw(shape);
}
Now if someone as the user of my library defines IRectangleShape and wants to draw it, they can simply define IRectangleShapeDrawer and add it to ShapeDrawingClient's list of drawers!