Carlos Obregón
Java Champion with 15 years of experience in software programming
¿Qué es el diseño orientado por objetos? ¿De qué se trata? ¿Cuáles son sus beneficios? ¿Cuáles son sus costos?
Parece tonto hacerse estas preguntas ahora que prácticamente todos los programadores usan un lenguaje orientado por objetos. Pero la pregunta
es importante porque, me parece, que la mayoría de nosotros utiliza esos lenguajes sin saber por qué y sin saber cómo sacarle el mayor beneficio posible.
public GridList(SlingHttpServletRequest request) {
ValueMap properties = request.getResource().adaptTo(ValueMap.class);
String leftLink = properties.get("leftlink", "");
String leftTitle = properties.get("lefttitle", "");
String leftDescription = properties.get("leftdescription", "");
this.leftGridBlock = new GridBlockDialog(leftLink, leftTitle, leftDescription);
String rightLink = properties.get("rightlink", "");
String rightTitle = properties.get("righttitle", "");
String rightDescription = properties.get("rightdescription", "");
this.rightGridBlock = new GridBlockDialog(rightLink, rightTitle, rightDescription);
}
public GridList(SlingHttpServletRequest request) {
ValueMap properties = request.getResource().adaptTo(ValueMap.class);
GridBlockDialog[] blocks = new GridBlockDialog[2];
String[] values = {"left", "right"};
for (int i = 0; i < blocks.length; ++i) {
String link = properties.get(values[i] + "link", "");
String title = properties.get(values[i] + "title", "");
String desc = properties.get(values[i] + "description", "");
blocks[i] = new GridBlockDialog(link, title, description);
}
this.leftGridBlock = blocks[0];
this.rightGridBlock = blocks[1];
}
You can also use functions!public GridList(SlingHttpServletRequest request) { ValueMap properties = request.getResource().adaptTo(ValueMap.class);
this.leftGridBlock = new GridBlockDialog(properties, "left"); this.rightGridBlock = new GridBlockDialog(properties, "right");
}
public String square(int size) { StringBuilder builder = new StringBuilder(); for (int row = 1; row <= size; ++row) { for (int column = 1; column <= size; ++row) { builder.append('*'); } builder.append(String.format("%n"); } return builder.toString();
} public String pyramid(int size) { StringBuilder builder = new StringBuilder(); int totalSpaces = (2 * size) - 1; int totalCharacters = 1; for (int row = 1; row <= size; ++row) { for (int spaces = 1; spaces <= totalSpaces; ++spaces) { builder.append(' '); } for (int chars = 1; chars <= totalCharacters; ++chars) { builder.append('*'); } --totalSpaces; totalCharacters += 2; } return builder.toString(); }
What do we do?public static String square(int size) { StringBuilder builder = new StringBuilder(); for (int row = 1; row <= size; ++row) { for (int column = 1; column <= size; ++column) { builder.append('*'); } } return builder.toString(); } public static String pyramid(int size) {
int totalSpaces = (2 * size) - 1; int totalCharacters = 1; StringBuilder builder = new StringBuilder(); for (int row = 1; row <= size; ++size) { for (int spaces = 1; spaces <= totalSpaces; ++spaces) { builder.append(' '); } for (int chars = 1; chars <= totalCharacters; ++chars) { builder.append('*'); } --totalSpaces; totalCharacters += 2; } return builder.toString(); }
private static final char SPACE = ' ';
private static final char CHARACTER = '*';
private static String makeString(int size, char character) {
StringBuilder builder = new StringBuilder();
for (int n = 1; n <= size; ++n) {
builder.append(character);
}
return builder.toString();
}
Use constants to get rid of magic numbers! If their value change there is just one place to update it!What about now?public static String square(int size) { StringBuilder builder = new StringBuilder(); for (int row = 1; row <= size; ++row) { builder.append(String.format("%s%n",makeString(size, CHARACTER))); } return builder.toString(); }
public static String pyramid(int size) { int totalSpaces = (2 * size) - 1; int totalChar = 1; StringBuilder builder = new StringBuilder(); for (int row = 1; row <= size; ++size) { builder.append(makeString(totalSpaces, SPACE)); builder.append(String.format("%s%n",makeString(totalChar, CHARACTER))); --totalSpaces; totalChar += 2; } return builder.toString(); }
private static String makeFigure(int size, int spaces, int chars, int modifierSpaces, int modifierChar) {
StringBuilder builder = new StringBuilder();
for (int row = 1; row <= size; ++size) {
builder.append(makeString(spaces, SPACE));
builder.append(String.format("%s%n",makeString(chars, CHARACTER)));
totalSpaces += modifierSpaces;
totalChar += modifierChar;
}
return builder.toString();
}
And with this abstraction, our code to "draw" figures ispublic static String square(int size) {
return makeFigure(size, 0, size, 0, 0);
}
public static String pyramid(int size) {
return makeFigure(size, 2 * size - 1, 1, -1, 2);
}
public class Fraction {
private int denominator; private int numerator;
private static int gcf(int a, int b) { return b == 0 ? a : gcf(b, a % b); }
public Fraction(int numerator, int denominator) {
final int GCF = gcf(numerator, denominator); this.denominator = denominator / GCF; this.numerator = numerator / GCF; }
}
public class IntStack {
private int i = 0;
private int[] stack = new int[10];
public void push(int n) {
stack[i++] = n; // has 100% cohesion
}
public int pop() {
return stack[i--]; // has 100% cohesion
}
public int size() {
return i + 1; // has 50% cohesion
}
}
This class doesn't break SRPpublic class Fraction {
private int numerator; private int denominator;
public Fraction(int numerator, int denominator) { final int GCF = ArithmeticOperations.gcf(numerator, denominator); this.numerator = numerator / GCF; this. denominator = denominator / GCF; }
}
public Config {
public Config(Path filename) throws IOException {
List<String> content = Files.readAllLines(filename);
// more code.
}
// more code.
}
public Config {
public class Config(List<String> content) {
// code.
}
// more code.
}
public void draw(List<Shape> shapes) {
for(Shape s : shapes) {
if (s instanceof Circle) {
// code specific to drawing a Circle
} else if (s instanceof Square) {
// code specific to drawing a Square
}
// more if statements for other shapes
}
}
Every time a new figure is created Now draw works with all Shapes, even onces that have been not created!public class Circle extends Shape { // code public void draw() { // code specific to draw a Circle } }
public class Square extends Shape { // code public void draw() { // code specific to draw a Square } } ... public void draw(List<Shape> shapes) { for (Shape s : shapes) { s.draw(); } }
public enum Number {
ACE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, J, Q, K;
public int value() {
return ordinal() + 1;
}
}
public enum Suit {
HEARTS, DIAMONDS, SPADES, CLUBS;
}
A Card also have a value, should we try public class Card {
private Number number; private Suit suit;
public Card(Number number, Suit suit) { this.number = number; this.suit = suit; }
public int value() { return number.value(); // Composition + Delegation } }
public class Ellipsis {
private int a; private int b;
public Ellipsis(int a, int b) { this.a = a; this.b = b; }
public void setA(int a) { this.a = a; } public int getA() { return a; } public void setB(int b) {
this.b = b; } public int getB() { return b; }}
public class Circle extends Ellipsis {
}
public class Circle extends Ellipsis {
@Override public void setA(int a) {
this.a = a;
this.b = a;
}
@Override public void setB(int b) {
this.b = b;
this.a = b;
}
}
But now this code is brokenpublic void foo(Ellipsis e) {
e.setA(10);
e.setB(5);
assert e.getA() == 10;
}
...
foo(new Circle());
It may seem natural
but there are not exactly related:
A Timestamp is related to when something
happened in a local context, where a date
is something that should take into account
different calendars, timezones and offsets!
There may be a lot of reasons for which each class would need to evolve independently of each other!
- @gaijinco
By Carlos Obregón
Descripción de los principios SOLID, esenciales para escribir buen código OOP