Los métodos no deberían tener que llamarse en un orden determinado

Si para llamar a un método antes tienes que llamar a algún otro método… mal asunto. Algo no estás haciendo bien.

Vamos a usar como ejemplo una clase que envía un mensaje cifrado:

class Mensaje
{
    private string $mensajeOriginal = '';
    private string $mensajeCifrado = '';

    public function setMensaje($mensaje)
    {
        $this->mensajeOriginal = $mensaje;
    }

    public function cifrarMensaje($metodoCifrado)
    {
        switch ($metodoCifrado) {
            case 'md5':
            default:
                $this->mensajeCifrado = md5($this->mensajeOriginal);
                break;
            case 'sha':
                $this->mensajeCifrado = sha1($this->mensajeOriginal);
                break;
        }
    }

    public function enviar()
    {
        return "Mensaje cifrado enviado: " . $this->mensajeCifrado;
    }
}

$mensaje = new Mensaje;
$mensaje->setMensaje("Este mensaje no debe ser leído.");
echo $mensaje->enviar() . PHP_EOL;

Aparentemente está todo correcto ¿no? Mmm, prueba a ejecutar este código (que, por cierto, necesita de la versión de PHP7.4 o superior).

El resultado será que no se «envía» el mensaje cifrado. Éste está en blanco ¿Y por qué? Pues porque no hemos llamado al método cifrarMensaje().

Puede que parezca una tontería ¿quién se va a olvidar cifrar el mensaje? Pues resulta que es muy habitual hacerlo. Somos así.

¿Y si controlamos si hay mensaje cifrado?

Bueno, esta podría parecer una solución. Algo así:

    public function enviar()
    {
        if(!$this->mensajeCifrado) {
            return "ERROR: El mensaje no se ha cifrado!";
        }
        return "Mensaje cifrado enviado: " . $this->mensajeCifrado;
    }

¿Qué problema hay con ésto? Pues que en realidad no estamos solucionando el problema. Le avisamos a la persona que use nuestra clase que en su código se ha olvidado de llamar al método cifrarMensaje(). Pero nada impide que se cuele un error.

Además, también podría suceder que nos olvidemos de llamar a setMensaje().

Estamos viendo que esta clase nos puede dar multitud de problemas porque no podemos usarla (el método enviar()) hasta que no hayamos llamado a los métodos setMensaje() y cifrarMensaje().

Una clase siempre debería estar un estado «usable».

Pasarlo todo en el constructor

Una posible opción sería la de pasar toda la información necesaria al constructor:

class Mensaje
{
    private string $mensajeOriginal = '';
    private string $mensajeCifrado = '';

    public function __construct($metodoCifrado, $mensaje)
    {
        $this->metodoCifrado = $metodoCifrado;
        $this->mensajeOriginal = $mensaje;

        $this->cifrarMensaje();
    }

    private function cifrarMensaje()
    {
        switch ($this->metodoCifrado) {
            case 'md5':
            default:
                $this->mensajeCifrado = md5($this->mensajeOriginal);
                break;
            case 'sha1':
                $this->mensajeCifrado = sha1($this->mensajeOriginal);
                break;
        }
    }

    public function enviar()
    {
        return "Mensaje cifrado enviado: " . $this->mensajeCifrado;
    }
}

$mensaje = new Mensaje('sha1', "Este mensaje no debe ser leído.");
echo $mensaje->enviar() . PHP_EOL;

De esta forma, cuando vayamos a enviar el mensaje ya tendremos todo lo necesario.

Quiero más libertad

Es posible que te parezca una clase muy poco flexible porque para enviar otro mensaje tendrías que crear una nueva clase. Pues también tendrías la opción de meter el método de cifrado en el constructor y el mensaje en el método enviar(). De esa forma puedes enviar varios mensajes siempre y cuando todos usen el mismo método de cifrado.

O también podrías pasarlo todo en el método enviar(). Dependerá un poco de lo que necesites. Este último caso sería algo así:

class Mensaje
{
    private string $mensajeOriginal = '';
    private string $mensajeCifrado = '';

    private function cifrarMensaje()
    {
        switch ($this->metodoCifrado) {
            case 'md5':
            default:
                $this->mensajeCifrado = md5($this->mensajeOriginal);
                break;
            case 'sha1':
                $this->mensajeCifrado = sha1($this->mensajeOriginal);
                break;
        }
    }

    public function enviar($metodoCifrado, $mensaje)
    {
        $this->metodoCifrado = $metodoCifrado;
        $this->mensajeOriginal = $mensaje;

        $this->cifrarMensaje();

        return "Mensaje cifrado enviado: " . $this->mensajeCifrado;
    }
}

$mensaje = new Mensaje();
echo $mensaje->enviar('sha1', "Este mensaje no debe ser leído.") . PHP_EOL;

Bueno, y si me conoces un poco ya sabrás que ese switch que hay en cifrarMensaje() no me gusta nada. Así que otro día explicaré cómo hacer para poder añadir nuevos métodos de cifrado sin tocar nada en la clase Mensaje.

Me siento solo, dime algo...