I'm writing an API for creating geometric shapes, and I'm running into some difficulties naming my methods.
Let's take a simple case: Creating a circle. Most of us might be familiar with a method like graphics.drawEllipse(x, y, w, h). To draw a circle, you need to know the top left coordinate, and the width and height of the circle.
My API is intended to make it easy for a developer to draw shapes using a variety of information, without doing a lot of math - which is trivial for circles, but more complicated for other shapes. For example, you should also be able to draw a circle given its center coordinates and radius, or the top left and bottom right coordinates.
So I have a Circle class with factory methods like:
Circle.createWithCenterAndRadius(cx, cy, r)
Circle.createWithBoundingBox(x1, y1, x2, y2)
Circle.createWithWidthAndHeight(x, y, w, h)
I feel like there might be a "code smell" here, but I'm not sure. On the one hand, these factory methods are necessarily descriptive. On the other hand, I can forsee these method names getting out of control. For example, how would I name a Triangle factory method that creates a triangle given a point, the length of one side, an angle, and the length of another side? Triangle.createWithPointSideAngleAndSide(x, y, side1, angle, side2)? Is that just evil?
If you were to use this API, would method names like this be okay to you? Do you have advice on how I can make the method names more sane?
You might change your circle methods to
Circle.FromCenterAndRadius(...)
Circle.FromBoundingBox(...)
Circle.FromWidthAndHeight(...)
It implies that you're creating circles from their different representations in a kind of concise way...
It is ok in any language that doesn't support named parameters. If the language supports named parameters, I like more the short Create and just have obvious parameters names.
For a language with named parameters, you would:
Circle.Create(
centerX = cx,
centerY = cy,
radius = r
);
Another more involved option, would be a fluent interface like (but that is probably too much):
circleBuilder.Center(cx,cy).Radius(r)
circleBuilder.Center(x,y).Width(w).Height(y)
circleBuilder.BoundWith().Left(x1,y1).Right(x2,y2)
Center returns an instance of an intermediate class that only allows Radius or Width. And BoundWith returns one that only allows Left.
I think there is nothing wrong with your descriptive methods - they are the compact and describe exactly what's going on. The users of the library will have no doubt about the function of your methods, neither the maintanance programmers.
You could also apply some design pattern here if you are really worried about exposing a large number of factory methods - like having factory methods with property classes. You could have a CircleProperties class with properties like CenterX, CenterY, Radius, (bool)UseCenterX, (bool)UseCenterY etc and then you pass this to the public factory method which will figure out which (private) factory method to use.
Assuming C#:
var circleProperties = new CircleProperties()
{
CenterX = 10,
CenterY = -5,
Radius = 8,
UseCenterX = true,
UseCenterY = true,
UseCenterRadius = true
};
var circle = Circle.Create(circleProperties);
My first instinct is to have more types, which would allow for more intuitive method overloading.
// instead of Circle.createWithCenterAndRadius(cx, cy, r)
Circle.create( new Point(cx,xy), r);
// instead of Circle.createWithBoundingBox(x1, y1, x2, y2)
Circle.create( new Point(x1,y1), new Point(x1,y1) );
// or even...
Circle.create( new Box(p1,p2));
// instead of Circle.createWithWidthAndHeight(x, y, w, h)
Circle.create( new Point(x,y), w, h);
As well as Point, you could define Distance (which would allow for different units)
If this style suits you, consider why you need a factory method instead of a constructor.
Circle c = new Circle(new Point(cx,xy), r);
For languages that don't support named parameters, would it be cleaner to make the method name something very simple like Circle.create and then just add an additional input flag string (like "center" or "bounding") that indicated how the input values should be interpreted for cases that are hard to discriminate based only on input variable number and type? Drawbacks to this would be that it requires extra logic inside of the method to handle different types of input arguments and also requires that the user remember the flag options.
I would have methods CreateTriangle and have the overloads show the different pieces of information required.
E.g.
Circle.CreateCircle(cx, cy, r)
Circle.CreateCircle(point1, point2)
Circle.CreateCircle(point, width, height)
Yes, this is more of a meta-answer, but I suggest you take a peek at how naming is done in Apple's Cocoa.
Your instinct is correct--the entire pattern of creating things this way is--iffy.
Unless these are used just once or twice, they are going to become pretty messy. If you were creating a shape with 5 circles and 3 triangles, it would be a mess.
Anything beyond a trivial example would probably be best done with some kind of data-driven implementation.
Towards those ends, having it take a string, hash or XML to define your shapes might be extremely useful.
But it all depends on how you expect them to be used.
I have the same kind of issues with creating Swing controls in Java. You end up with line after line of "new Button()" followed by a bunch of .set property calls as well as a line of code to copy the value to an object (or add a listener), and a line to reset the value..
That kind of boilerplate should never happen in code, so I usually try to find a way to drive it with data, binding the controls to objects dynamically--and towards that end, a descriptive string-based language would be very helpful.
I know, I know. This sounds completely crazy for you C/C++/Java people, but the examples given in the question and in all those answers clearly demonstrate what a bad, bad convention CamelCaseNaming really is.
Let's take another look at the original example:
Circle.createWithCenterAndRadius(cx, cy, r)
Circle.createWithBoundingBox(x1, y1, x2, y2)
Circle.createWithWidthAndHeight(x, y, w, h)
And now let's get rid of that camel case notation
Circle.create_with_center_and_radius(cx, cy, r)
Circle.create_with_bounding_box(x1, y1, x2, y2)
Circle.create_with_width_and_height(x, y, w, h)
This may seem terribly unfamilar, but be honest: which version is easier to read?
Related
I recently read that option-operand separation is a principle that was introduced in the Eiffel language (I've never used Eiffel).
From the Wikipedia article:
[Option–operand separation] states that an operation's arguments should contain only operands — understood as information necessary to its operation — and not options — understood as auxiliary information. Options are supposed to be set in separate operations.
Does this mean that a function should only contain "essential" arguments that are part of its functionality, and that there shouldn't be any arguments that change the functionality (which instead should be a separate function)?
Could someone explain it simply, preferably with pseudocode example(s)?
Yes, this is the idea: arguments should not be used to select particular behavior. Different methods (features in Eiffel terms) should be used instead.
Example. Suppose, there is a method that moves a 2-D figure to a given position. The position could be specified using either polar or Cartesian coordinates:
move (coordinate_1, coordinate_2: REAL_64; is_polar: BOOLEAN)
-- Move the figure to the position (coordinate_1, coordinate_2)
-- using polar system if is_polar is True, and Cartesian system otherwise.
According to the principle, it's better to define two functions:
cartesian_move (x, y: REAL_64)
-- Move the figure to the position with Cartesian coordinates (x, y).
polar_move (rho, phi: REAL_64)
-- Move the figure to the position with polar coordinates (rho, phi).
Although the principle seems to be universally applicable, some object-oriented languages does not provide sufficient means for that in certain cases. The obvious example are constructors that in many languages have the same name, so using options becomes the only choice (a workaround would be to use object factories in these cases).
I have two data models which are represented by the following classes:
1) ImagesSet - an object that owns 2DImage's, each 2DImage has its own position (origin(3DPoint), x-,y-axes(3DVector) and dimension along x and y axes(in pixels)), but the same pixel size(in mm for example), angle between x and y axes(90 degrees)
This object has following methods(in pseudo code):
AddImage(2DImage);
RemoveImage(ImageIndex);
number GetNumberOfImages();
2DImage Get2DImage(ImageIndex);
2) 3DImage - an objects that is similar to the first but with following restrictions:
it can store 2D images only with the same x-,y-axes and dimensions along x and y axes.
Is it correct in this case to derive 3DImage from ImagesSet?
From my point of view 3DImage "is a" ImagesSet (but with small restrictions)
Could I apply here Liskov substitution principle?
In this case if we are trying to add an image with another x,y axes - method AddImage either will throw an exception or return an error.
Thanks in advance,
Sergey
I agree with maxim1000 that LSP will be violated because derived class adds restrictions that are not present in the base class. If you take a close look at your description you will notice that the question can be turned upside-down: Can ImageSet derive from 3DImage?
Your situation is somewhat similar to Ellipse-Circle problem. Which one derives from the other? Is circle an ellipse with a constraint, or is an ellipse a circle with additional radius? The point is that both are wrong. If you constrain ellipse to equal radiuses, then client which attempts to set different values would receive an error.
Otherwise, if we say that ellipse is just a less constrained circle, we get a more subtle mistake. Suppose that shapes may not breach boundaries of the screen. Now suppose that a circle is replaced with an ellipse. Depending on which coordinate was tested, the shape might break out of the screen area without changing the client code. That is the exact violation of LSP.
Conclusion is - circle and ellipse are separate classes; 3DImage and ImageSet are separate classes.
May be it's just me, but whenever I hear "derive or not derive" my first reaction "not derive" :)
Two reasons in this case:
LSP is violated exactly because of those "small restrictions". So until you have AddImage in your base class which allows to add an image with any orientation, 3DImage is not an ImagesSet. There will be no way for algorithms to state that they need this feature (and comments is not a good place :) ), so you'll have to rely on run-time checks. It's still possible to program in this way, but this will be one more overhead for developers.
Whenever you create some abstraction, it's important to understand why exactly it's created. With derivation you implicitly create an abstraction - it's interface of 3DImage. And instead of this it's better to create this abstraction explicitly. Create an interface class, list there methods useful for algorithms able to work on both data structures and make both ImagesSet and 3DImage implementing that interface possibly adding some other methods.
P.S.
And likely AddImage will become one of those added methods - different in ImagesSet and 3DImage, but that depends...
Dear maxim1000 and sysexpand,
Thanks for the answers. I agree with you. It is clear now that LSP is violated and in this case I can't derive 3DImage from ImagesSet.
I need to redesign the solution in the following way:
2DImage will contain:
2DDimension's
PixelSize(in mm)
PixelData
2DImageOrientated will be derived from 2DImage and will contain new data:
3DPoint origin,
3DVector x-,y-axes
I will create pure interface IImagesSet:
number GetNumberOfImages()
RemoveImage(ImageIndex)
2DImageOrientated Get2DImage()
ImagesSet will be derived from IImagesSet and will contain the following:
vector<2DImageOrientated>
Add2DImage(2DImageOrientated)
number GetNumberOfImages()
RemoveImage(ImageIndex)
2DImageOrientated Get2DImage()
3DImage will be also derived from IImagesSet and will contain the following.
vector<2DImageOrientated>
Add2DImage(2DImage)
SetOrigin(3DPoint)
SetXAxis(3DVector)
SetYAxis(3DVector)
number GetNumberOfImages()
RemoveImage(ImageIndex)
2DImageOrientated Get2DImage()
In this case I think LSP is not violated.
I'm reading a lot of things about "correctness proof"* in algorithms.
Some say that proofs apply to algorithms and not implementations, but the demonstrations are done with code source most of the time, not math. And code source may have side effects. So, i would like to know if any basic principle prevent someone to prove an impure function correct.
I feel it's true, but cannot say why. If such principle exists, could you explain it ?
Thanks
* Sorry if wording is incorrect, not sure what the good one would be.
The answer is that, although there are no side effects in math, it is possible to mathematically model code that has side effects.
In fact, we can even pull this trick to turn impure code into pure code (without having to go to math in the first place. So, instead of the (psuedocode) function:
f(x) = {
y := y + x
return y
}
...we could write:
f(x, state_before) = {
let old_y = lookup_y(state_before)
let state_after = update_y(state_before, old_y + x)
let new_y = lookup_y(state_after)
return (new_y, state_after)
}
...which can accomplish the same thing with no side effects. Of course, the entire program would have to be rewritten to explicitly pass these state values around, and you'd need to write appropriate lookup_ and update_ functions for all mutable variables, but it's a theoretically straightforward process.
Of course, no one wants to program this way. (Haskell does something similar to simulate side effects without having them be part of the language, but a lot of work went into making it more ergonomic than this.) But because it can be done, we know that side-effects are a well-defined concept.
This means that we can prove things about languages with side-effects, provided that their specifications provide us with enough information to know how to rewrite programs in them into state-passing style.
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 10 years ago.
How to use polymorphism in functional programming (with dynamic type system)?
Let's consider following example (first in OOP second in FP). The program is very simple - there are list of figures and we need to draw all of them, different figures use different drawing algorithms.
In OOP it can be done trivially, but how to do it in FP? Especially in languages with dynamic type system, like Scheme, Clojure (without static type resolving at compile time)?
I created simple code ( live version http://tinkerbin.com/0C3y8D9Z , press 'Run' button ). I used if/else switch in FP sample, but it's a very bad approach. How such problem can be solved better?
Samples are in JavaScript, but it's only for purpose of simplicity, it would be interesting to see a solution in any functional language with dynamic typing system.
OOP
var print = function(message){document.write(message + "\n<br/>")}
// Object Oriented Approach.
var circle = {
draw: function(){print("drawing circle ...")}
}
var rectangle = {
draw: function(){print("drawing rectangle ...")}
}
var objects = [circle, rectangle]
objects.forEach(function(o){
o.draw()
})
FP
var print = function(message){document.write(message + "\n<br/>")}
// Functional Approach.
var circle = {type: 'Circle'}
var drawCircle = function(){print("drawing circle ...")}
var rectangle = {type: 'Rectangle'}
var drawRectangle = function(){print("drawing rectangle ...")}
var objects = [circle, rectangle]
objects.forEach(function(o){
if(o.type == 'Circle') drawCircle(o)
else if(o.type == 'Rectangle') drawRectangle(o)
else throw new Error('unknown type!')
})
Your "FP" version is not what I consider the idiomatic FP example. In FP, you often use variants and pattern matching where in OOP you'd use classes and method dispatch. In particular, you only have one draw function that already does the dispatch internally:
var circle = {type: 'Circle'}
var rectangle = {type: 'Rectangle'}
var draw = function(shape) {
switch (shape.type) {
case 'Circle': print("drawing circle ..."); break
case 'Rectangle': print("drawing rectangle ..."); break
}
}
var objects = [circle, rectangle]
objects.forEach(draw)
(Of course, that is JavaScript. In a functional language you typically have much more elegant and concise syntax for this, e.g.:
draw `Circle = print "drawing circle..."
draw `Rectangle = print "drawing rectangle..."
objects = [`Circle, `Rectangle]
foreach draw objects
)
Now, the average OO aficionado will see the above code and say: "But the OO solution is extensible, the above is not!" That's true in the sense that you can easily add new shapes to the OO version and don't have to touch any of the existing ones (or their draw functions) when you do. With the FP way, you'd have to go in and extend the draw function, and all other operations that may exist.
But what those people fail to see is that the converse is also true: the FP solution is extensible in a way the OO one isn't! Namely, when you add a new operation over your existing shapes then you don't need to touch any of the shape definitions, nor existing operations. You simply add another function, whereas with OO, you have to go and modify every class or constructor to include an implementation for the new operation.
That is, there is a dualism here in terms of modularity. The ideal, achieving simultaneous extensibility along both axes is known in literature as "the expression problem", and while various solutions exist (especially in functional languages), they are typically more complex. Hence, in practice you'll often want to decide for one dimension, depending on which dimension is more likely to matter for the problem at hand.
There are other advantages to the functional version. E.g it trivially scales to multi-dispatch or more complex case distinctions. It also is preferable when implementing an algorithm that is complicated and where the different cases are interrelated, so that you want to have the code in one place. As a rule of thumb, whenever you start using the visitor pattern in OO, a functional-style solution would have been more appropriate (and far, far easier).
Some further remarks:
This different preference in program organisation isn't the central idea of FP. What matters more is discouraging mutable state, and encouraging highly reusable higher-order abstractions.
The OO community has this habit of inventing new (buzz)words for every old idea. Its use of the term "polymorphism" (which is completely different from what it means elsewhere) is one such example. It says little more than being able to call functions without statically knowing what the callee is. You can do that in any language where functions are first-class values. In that sense, your OO solution is perfectly functional as well.
Your question has very little to do with types. Both the idiomatic OO and the idiomatic FP solution work in an untyped or typed language.
OO polymorphism is not a part of functional programming. However some functional languages (e.g. clojure) have oo polymorphism.
Another kind of polymorphism is multimethods
(def circle {:type :circle
:radius 50})
(def rectangle {:type :rectangle
:width 5
:height 10})
(defmulti draw :type)
(defmethod draw :circle [object]
(println "circle: radius = " (:radius object)))
(defmethod draw :rectangle [object]
(println "rectangle: "
"width = " (:width object)
"height = " (:height object)))
(doseq [o [rectangle circle]] (draw o))
=> rectangle: width = 5 height = 10
circle: radius = 50
Or you just can use functional style
(defn circle [] (println "drawing circle ..."))
(defn rectangle [] (println "drawing rectangle ..."))
(def objects [circle rectangle])
(doseq [o objects] (o))
=> drawing circle ...
drawing rectangle ...
In Clojure there are Protocols which provide basically the same ad-hoc polymorphism as Haskell's type classes:
(defprotocol shape (draw [e]))
(defrecord circle [radius])
(defrecord rectangle [w h])
(extend-protocol shape
circle (draw [_] "I am a nice circle")
rectangle (draw [_] "Can I haz cornerz please?"))
You can also extend an existing type:
(extend-protocol shape
String (draw [_] "I am not a shape, but who cares?"))
And then you can apply the draw method to some instances
user=> (map draw [(->circle 1) (->rectangle 4 2) "foo"])
("I am a nice circle" "Can I haz cornerz please?" "I am not a shape, but who cares?")
There really isn't anything non-functional about your first code sample. Even in languages that have no support for object orientation, you can do the same thing. That is you can create a record/structure/map that contains functions and then put those into your list.
In your simple example where there's only one function, you could also just create a list of functions directly, like objects = [drawCircle, drawRectangle].
In several languages designed primarily for functional programming, there are ways to achieve (ad-hoc, as it is called) polymorphism, though they differ from what you call polymorphism. Haskell, for example, has type classes (not to be confused with classes from classical OOP):
class Draw a where
draw :: a -> SomethingSomthing -- probably IO () for your example, btw
(Scala has objects, and also implicits which apparently parallel or even surpass type classes.)
You can then implement any number of independent types, and make each an instance of the type class (again independently, e.g. in an entirely different module):
data Circle = Circle Point Double -- center, radius
data Rectangle = Rect Point Double Double -- center, height, width
instance Draw Circle where
draw (Circle center radius) = …
instance Draw Rectangle where
draw (Rect center height width) = …
This is probably what you'd use in Haskell, if you actually needed that degree of extensibility. If you have a finite number of cases belonging together (i.e. you could use sealed classes in the OOP alternative), you'd likely use algebraic data types (see below).
Another way is to do just what your JS snippet does (which is, by the way, not what you'd do to achieve polymorphism if you had any number of objects of each type, and this version has the same problem): Embed a function that does the polymorphic behavior in each object. In a sense, your "OOP" snippet is already functional.
data Drawable = Drawable (Drawable -> SomethingSomething) {- other fields -}
draw (Drawable draw) = draw Drawable
Though in a static language, this does not permit different objects to have different attributes.
A more bearable alternative to the bunch of conditions you present, but nevertheless similar and with the same limitation (it's hard to add another shape), is pattern matching with algebraic data types. Other answers on Stackoverflow have explained these well, I'll just give this concrete example in that style:
data Shape = Circle {- see second snippet -}
| Rect {- ditto -}
draw (Circle center radius) = …
draw (Rect center height width) = …
This is one of the boring academic OOP questions, but it is not a homework. I got the question from a newbie programmer about one of those stupid textbooks examples about OOP.
Imagine that you are designing a Square class and a Cube class, which should inherit which?
I see a relationship, but what it is, I can not really see!
Could you give me a logical argument with OOP in mind.
Neither! Since a square is not a cube, and a cube is not a square, neither should inherit from the other. Square can inherit from polygon and cube can inherit from polyhedron, but the two are, themselves, mutually exclusive.
There's no inheritance. Inheritance is a "is-a" relationship (Well, sometimes not even a "is-a" relationship, as mentioned in the link below). A cube is not a square, and a square is not a cube.
How would you construct, it depends on how you model. You could go with something like a cube has 6 squares (a cube is not, it has 6 squares; a composition), or a cube has a side size, just like square. But once there is no "is-a", an inheritance would be dangerous zone...
Also, in a inheritance, everything that is valid for the base class must be valid for the Derived one. It's the Square extends Rectangle problem. For instance:
Supposing Cube inherits Square: If your Square has a method changeArea(double area) and getSide(), the same should be possible for the Cube. But it is not, since the area of a cube is 6 times the area of a square (it has 6 squares).
Supposing Square inherits Cube: If your Cube has a setVolume(double volume) method, your square will be broken, once it has no volume
Finally, if you want to use inheritance, you could create a GeometryObjectWithEqualSides object, then both could inherit from it. :)
Having it either way would be a violation of the Liskov Substitution Principle.
Both inherit from hypercube
struct Square // Rectangle actually
{
Square( int dx, int dy ) : dx(dx), dy(dy) {};
int dx;
int dy;
int floor_area() { return dx*dy; };
};
struct Cube : Square // Cuboid actually
{
Cube( int dx, int dy, int dz ) : Square(dx, dy), dz(dz) {};
int dz;
int cube_area() { return floor_area()*2+dx*dz*2+dy*dz*2; };
};
Seems to be that Liskov substitution principle is not violated here.
The square and the cube could be argued to be two instances of the same class "Hypercube" which would also encompass the point (0 dimensions), the line segment (1 dimension) and others beyond that. The number of dimensions and the length of one side are enough to define a specific instance of Hypercube (you could of course add an n-dimensional point of origin and orientation).
The hypercube could provide functions/methods that return values for the number of vertices, edges, faces, etc. for that particular instance.
See Hypercube on Wikipedia for more info.
Neither should inherit the other. One is a 2-dimensional shape, the other is a 3-dimensional object. There really isn't enough similarity between the two to justify inheritance.
Now you might conceivably make a cube composed of squares, if you need a separate object for each side ;)
Most of comments here are correctly saying that none of them should inherit from another. This is true in most cases. But I think there is more generic answer:
It depends on your expectations on them.
You expect Square to do what? Does Cube also do it? May be the other way - can you use Square whenever you use Cube?
I think both statements "Cube does all what Square does" and "Square does all what Cube does" are false, according to common sense, so none of them should inherit from another. However, it is up to you to decide their structure and behavior, because it is you who defines what your program does and what it consists of.
Most likely "Cube contains 6 Squares" is the relationship that you saw.