Esta es el arreglo más correcta que te podemos aportar, pero mírala detenidamente y valora si se adapta a tu proyecto.
Solución:
La refactorización Reemplazar condicional con polimorfismo es más efectiva cuando ve el mismo condicional disperso a lo largo de su código. Cuando necesite agregar un nuevo tipo de comportamiento, debe encontrar y cambiar cada condicional para acomodar la nueva opción. En cambio, concentramos la lógica condicional en un solo lugar, el código que crea el objeto polimórfico, y dejamos que la semántica de OO se encargue del resto por nosotros.
Aquí hay una forma de hombre de paja más atroz de su ejemplo de registro.
if log_type == "file":
log_file.write("DEBUG: beginning script")
elif log_type == "database":
cursor.execute("INSERT INTO log_table (Level, Msg) VALUES ('DEBUG', 'beginning script')")
try:
file = open("/path/to/file")
lines = file.readlines()
if log_type == "file":
log_file.write("INFO: read lines".format(len(lines)))
elif log_type == "database":
cursor.execute("INSERT INTO log_table (Level, Msg) VALUES ('INFO', 'read lines')".format(len(lines)))
except:
if log_type == "file":
log_file.write("ERROR: failed to read file")
elif log_type == "database":
cursor.execute("INSERT INTO log_table (Level, Msg) VALUES ('ERROR', 'failed to read file')")
raise
finally:
if log_type == "file":
log_file.write("INFO: closing file")
elif log_type == "database":
cursor.execute("INSERT INTO log_table (Level, Msg) VALUES ('INFO', 'closing file')")
file.close()
Puede ver que la lógica condicional que examina el tipo de registro se ejecuta tres veces, sutilmente diferente cada vez. Si necesitáramos agregar un nuevo tipo de registro, como registrar errores por correo electrónico, tendríamos que revisar todo el script y agregar otro elif
a cada declaración de registro, que es propensa a errores y engorrosa.
También es bastante difícil ver de un vistazo lo que realmente está haciendo el script, porque está inundado de detalles sobre cómo hacer el registro.
Así que este es un gran candidato para Reemplazar condicional con polimorfismo. Aquí están las clases de registrador después de la refactorización:
class AbstractLogger:
def debug(self, msg):
self.log("DEBUG", msg)
def info(self, msg):
self.log("INFO", msg)
def error(self, msg):
self.log("ERROR", msg)
def log(self, level, msg):
raise NotImplementedError()
class FileLogger(AbstractLogger):
def __init__(self, file):
self.file = file
def log(self, level, msg):
self.file.write(": ".format(level, msg))
class DatabaseLogger(AbstractLogger):
def __init__(self, cursor):
self.cursor = cursor
def log(self, level, msg):
self.cursor.execute("INSERT INTO log_table (Level, Msg) VALUES ('', '')".format(level, msg))
He usado la herencia para evitar repetir demasiado código entre las clases FileLogger y DatabaseLogger.
Aquí está el guión:
# create the logger once at the start
if log_type == "file":
logger = FileLogger(log_file)
elif log_type == "database":
logger = DatabaseLogger(cursor)
logger.debug("beginning script")
try:
file = open("/path/to/file")
lines = file.readlines()
logger.info("read lines".format(len(lines)))
except:
logger.error("failed to read file")
raise
finally:
logger.info("closing file")
file.close()
Ahora es mucho más fácil agregar un nuevo tipo de registro: simplemente escriba un EmailLogger
y modificar el condicional único que lo crea. El código también es mucho más limpio: las clases de registro ocultan todos los detalles de cómo funcionan detrás de un conjunto simple de métodos con nombres orientados al registro.