Fala Mestre, quanto tempo!
Então, eu acho bem interessante o chain of responsibility, porém algumas coisas me incomodaram em alguns códigos, queria saber se estou pensando muito errado ou me preocupando com bobagem demais.
No antigo treinamento sobre design patterns com php o instrutor fez uso de uma interface e um método setter para adicionar o próximo handler. No último handler que é o "SemDesconto" ele deixou o método "setProximo" vazio, o que é lógico já que é o último handler da cadeia, porém na minha opinião isso parecia ferir algumas boas práticas (segregação de interfaces talvez), já que tinha um método vazio apenas para cumprir com o contrato da interface. Gostaria de saber se isso chega a ferir alguma boa prática realmente. Aqui o link
No link que você deixa como estudo complementar, na parte de exemplo conceitual, quando o método handle não encontra mais uma condição me retorna um null, isso também me parece estranho, talvez viole o principio da substituição de liskov, já que a classe mãe ou subclasses dela podem tanto me retornar uma string como um null, ou seja, aprensetam comportamentos diferentes, mas posso estar muito enganado no meu entedimento.
De certo gostei mais da sua abordagem, o uso do nullable type ajudou na sua construção, mas parece não haver muito jeito em ter que fazer algo um pouco diferente quando se chega no último elo da cadeia.