-
-
Save plagelao/767336 to your computer and use it in GitHub Desktop.
require 'rspec' | |
class StringCalculator | |
def add(string) | |
return add(string_without_last_summand(string)) + last_summand(string) if more_than_one_summand?(string) | |
string.to_i | |
end | |
def string_without_last_summand(string) | |
string[0...last_separator_index(string)] | |
end | |
def last_summand(string) | |
string[last_separator_index(string) + 1..-1].to_i | |
end | |
def more_than_one_summand?(string) | |
string.include?(',') || string.include?("\n") | |
end | |
def separator | |
return /,|\n/ | |
end | |
def last_separator_index(string) | |
-1*(string.reverse.index(separator) + 1) | |
end | |
end |
las dos últimas katas las he hecho creando esa clase en vez de abriendo string y me he encontrado el mismo problema. La solución más obvia es la que te ha comentado eamodeorubio.
Pero si le añado un constructor a StringCalculator queda un poco raro usarlo:
StringCalculator.new("1,2,3").add
No mola mucho ¿no?
No ! Al contrario. Piensa que se llama StringCalculator, lo típico es que en futuro le añadas más métodos, e incluso también sobreescribir operadores y el método equal y hash code. Es el primer paso para cosas del estilo:
calculadora = StringCalculator.new("1,2,3")
calculadora.add == 6
calculadora.multiply == 6 # 1 * 2 * 3 = 6 Qué casualidad !
(calculadora + StringCalculator.new("10,20")).add == 36
Lo que pasa es que la kata es artificial, pero lo típico en un proyecto de verdad es que aparezcan más operaciones para el objeto de dominio.
Incluso si no aparecen nuevas operaciones, entonces tal es que el nombre del método no es el correcto. Cambia 'add' por 'sumaTotal' y entonces todo es más legible:
StringCalculator.new("1,2,3").sumaTotal.should == 6 # Ahora es más legible
Los nombres de los métodos también son importantes.
Creo que tienes razón. Parece bastante claro que string es una propiedad de la clase.
Una de las formas mas sencillas de hacer el string calculator si lo quieres hacer con una clase seria lo siguiente:
class StringCalculator
def self.add(string)
string.split(/,|\/n/).inject(0) {|sum, number| sum += number.to_i}
end
end
Si se diera el caso (como en la kata) de que tienes mas de un posible separador podrías hacer lo siguiente:
class StringCalculator
SEPARATORS = /,|\/n/
def self.add(string)
string.split(SEPARATORS).inject(0) {|sum, number| sum += number.to_i}
end
end
Yo te propondría dos acercamientos. Perdona si los hago en c#, ruby no lo controlo nada.
- Como dice eamodeorubio, string sería una propiedad de la clase. Lo cual te permitiria no tener que declararla en cada una de las firmas:
private string numerosACalcular {get; set;}
Así los métodos no necesitan tener en su firma el string de marras que, como bien dices, huele fatal.
Pero, y aquí viene lo bueno, al ser una propiedad, nos permite realizar el usar el patron de null object para tratar los casos en que el strin venga a nulo y que aun no tienes en cuenta en tu código. O, también, tratar el string para ver si cumple las condiciones necesarias como que se puedan obtener al menos dos números, etc.
Además permite decidir cuando y donde se introduce el string en la clase, desacoplando la clase del tipo de dato esperado, en este caso string.
¿Cómo? Creando un constructor estático de la clase. Así desacoplamos la clase y aumentamos el desacoplamiento de la misma:
public static int StringCalculator(string textoConNumeros);
{
numerosACalcular = textoConNumeros;
StringCalculator Calculador = new StringCalculator();
return Calculador.Add();
}
Y en Ruby, seguro que queda aún más compacto el código.
Espero haber sido de ayuda.
1.- No puedes usar NullObject con un string.
2.- Eso que has puesto no es un constructor. No sé que pretendes con ese código, pero... O_o
3.- No sé dónde ni de qué estás intentando desacoplar.
ja, ja, ja. Tienes razón Alberto, me explico como un libro cerrado. A ver si ahora me hago entender mejor.
- string cadena = (textoConNumero == null ? string.empty : textoConNumero) Patrón null object para tipo string. De echo, creo, este patron es para objetos, no importa el tipo del mismo. También se puede hacer de forma clásica con una variable privada y una propiedad pública, pero a mi me resulta más engorroso.
De todas formas es un tecnicismo, a lo que me quería referir es que la clase se podría mejorar si trata los objetos nulos o el que la cadena de texto no contenga números, o (si fuera c#) problemas de desbordamiento de enteros que no sé si en ruby hay que tenerlos en cuenta, etc. - Tienes toda la razón. Es confuso. Pero es aún más, en Ruby es inecesario, por lo que he podido leer, el tema de los métodos estáticos. Olvida el parrafo entero. En C# tiene sentido, en Ruby por lo poco que he leido, no.
- Estaba intentando evitar StringCalculator.new("1,2,3").add y convertirlo en StringCalculator.calcula("1,2,3") que me parece mucho más legible. Desacoplar la cadena de texto del constructor. Así se puede construir la clase/objeto no importando que las firmas sean diferentes. Ya se encargará la propia clase de gestionar cada una, ocultando el "como" se hacen las cosas y solamente devolviendo el resultado.
Por último, agradecerte la contestación porque me ha "picado" a leer un poquito de Ruby para poder intentar aclarar lo mal que me he explicado, y me he encontrado sorprendido con las diferencias. Y me ha causado mucha curiosidad.
De los 6 métodos que tiene StringCalculator, 5 reciben como argumento "string". Algo huele a podrido, pero no se como quitarme esa peste. ¿Me podéis ayudar?