O paradigma orientado a objetos é extremamente conveniente para os negócios: permite implementar praticamente qualquer idéia, fornecendo desempenho aceitável do produto. Nesse caso, por produto queremos dizer um aplicativo iOS; portanto, em conclusão, prosseguiremos com o desenvolvimento especificamente nesta plataforma.
Fechando os olhos para as falhas conhecidas desse paradigma popular, a lista de seus desvantagens inclui sua vantagem mais importante - a flexibilidade do desenvolvimento. Por que isso é um sinal de menos? É bastante óbvio que a flexibilidade, além da capacidade básica de resolver problemas de negócios, possibilita isso de várias maneiras. É verdade que existem uma dúzia de erros para uma abordagem correta, apesar do fato de que a tarefa de negócios será resolvida corretamente em qualquer um dos casos, mas com diferenças na implementação, cuja extensibilidade e transparência já dependerão da correção da abordagem aplicada.
Levando em conta a lei de Murphy, a conclusão é que, na ausência das restrições arquitetônicas corretas, é mais provável que siga o caminho do caos, ou seja, a qualidade do código diminuirá rapidamente, apenas porque o paradigma o permite. Este artigo discutirá uma das possíveis limitações arquitetônicas que ajudarão a manter o equilíbrio das forças do bem e do mal, ou seja, a dinâmica do crescimento da entropia na base de código do projeto. É importante que essa dinâmica se correlacione diretamente com o número de pessoas envolvidas na escrita do código; portanto, para grandes projetos, a correção das restrições selecionadas é especialmente crítica. Qual é o objetivo?
O ponto é simples. Vamos sair do próximo axioma - quanto mais propriedades houver em um objeto, mais "pior" será. Esta afirmação pode ser explicada da seguinte forma: com um aumento no número de estados internos, todos os indicadores positivos do objeto - extensibilidade, modularidade, transparência, testabilidade - diminuem. Pode-se objetar, dizendo que a complexidade do objeto e sua funcionalidade estão interconectados e que, sem o primeiro, o segundo não acontece. Tudo é verdade, mas, seguindo o caminho "stateful", o crescimento da complexidade ocorre exponencialmente, enquanto idealmente o crescimento deve ser linear ou, mais simplesmente, um novo recurso não deve complicar os recursos existentes.
PS Aqui, vale esclarecer que os recursos são entendidos como camadas semanticamente relacionadas da lógica de negócios; portanto, muitas vezes é tomada uma decisão para complicar uma classe existente, em vez de criar uma nova. Nesses casos, é difícil encontrar contradições com o primeiro princípio do SOLID.Um exemplo é uma tela completamente padrão com uma lista de entidades com uma escolha. Algumas das propriedades podem se tornar não estáveis, mas o IB nos liga ao construtor padrão do controlador e isso nos obriga a "fazer sujeira". O acesso a essas propriedades é obtido implicitamente por cada método de classe no mesmo arquivo. E o momento mais desagradável é que sua mutabilidade também não é coberta por nada, e isso de alguma forma leva a consequências irreversíveis na forma de uma conexão forte e, consequentemente, ao fato de que a fixação de alguns defeitos causa o aparecimento de novos:

Podemos concluir que um aumento linear na complexidade de um objeto com um estado comum é praticamente inatingível. A funcionalidade com essa abordagem se torna monolítica e difícil para qualquer tipo de segregação. Um bom exemplo é o Massive-View-Controller antipattern, que é bastante comum e demonstra claramente o resultado da ausência de quaisquer restrições ao projeto.

Usando o UIKit como exemplo, você pode ver exatamente como o estilo imperativo de desenvolvimento afeta a complexidade do código e em que pontos a estrutura “força” as propriedades a serem criadas nas classes.
O caso mais simples - o processamento de pressionar um botão - é realizado apenas de maneira padrão, definindo o “método sujo”, ou seja, por uma função que não pode receber nada de útil; portanto, você terá que sair para acessar as propriedades:

Assim, o "recurso" do botão complicará tiranicamente o restante da funcionalidade do objeto, uma vez que todos os residentes da classe terão acesso a esses dados. De fato, quase qualquer controle de interface do usuário do iOS funciona de maneira semelhante. Portanto, lembre-se de implementar algum tipo de wrapper sobre os elementos da interface do usuário, por exemplo, que encerre, como um processamento da "operação" de um elemento em particular, mas a dificuldade é descobrir como tornar esse wrapper conciso e confiável. Afinal, o assunto não está apenas na entrada, mas também na saída de informações, por exemplo, a tabela onipresente com dados também funciona "suja" e não tem idéia dos dados que mostra, então você deve salvá-la em um objeto:

É conveniente? Convenientemente, todos sempre têm acesso aos dados. Flexível, rápido, imperativo. Mas tudo se transforma ao longo do tempo, em regra, em um obelisco concreto para mil linhas de código e nas lágrimas daqueles que tiveram a tarefa de trabalhar nessa classe.
Voltando às limitações, o seguinte princípio pode ser formado a partir do código acima - sem propriedades nas classes é muito mais limpo e mais lucrativo com suporte e extensão. Idealmente, a lógica do objeto deve estar em seus métodos "puros", que tomam todas as suas dependências como entrada e têm o resultado de sua atividade na saída.
A idéia é diminuir o estado privado do objeto um nível abaixo. Ou seja, se o privado fecha propriedades do mundo exterior, nossa tarefa é ir ainda mais longe e fortalecer o encapsulamento ao nível dos próprios métodos. À primeira vista, por toda a natureza assíncrona da plataforma e a imperatividade da estrutura principal, pode parecer que toda essa idéia não seja apenas impossível, mas pelo menos sua implementação parecerá antinatural. E, muito provavelmente, será, mas esse é um daqueles problemas que podem ser resolvidos com a introdução de um nível adicional de abstrações.
Se queremos encaixar toda a lógica das classes em seus métodos sem recorrer às propriedades, precisamos trabalhar em estreita colaboração com os fechamentos. O IOS possui ferramentas padrão para gerenciar operações assíncronas, como GCD e OperationQueue. Parece que eles seriam suficientes, mas quando você tenta dar vida à ideia, tudo ficará longe de ser tão otimista quanto você gostaria. Com essa abordagem, além do fato de haver uma grande chance de um retorno de chamada, o código em si será complicado, terá muitos buracos lógicos e estará fortemente conectado. É possível que esse código seja ainda mais complicado do que estamos tentando escapar tão rapidamente.
Se você olhar em volta, verá que existem maneiras muito mais bonitas e funcionais de atingir esse objetivo. A programação reativa é utilizada há muito tempo no desenvolvimento comercial de software e é ideal para o mundo front-end assíncrono. Nesse caso, consideraremos uma implementação (bastante bem-sucedida) do paradigma reativo para Swift-Rx.

Ele oferece uma entidade simples chamada Observable. Esse é um tipo de abstração sobre o fluxo de eventos, pode ser inscrito e, depois disso, o assinante receberá esses eventos ao longo do tempo:

A maneira mais fácil de imaginar o fluxo de eventos é apresentando um botão regular. O evento de sua operação é aqui um fluxo, para que qualquer objeto possa se inscrever e receber eventos com um clique e, o mais importante, o botão não sabe nada sobre seus próprios assinantes. Convenientemente, quase qualquer ação pode ser transformada em uma seqüência semelhante de valores, e isso é importante, porque o Observable pode ser combinado entre si, uma vez que nenhuma estrutura padrão tornará isso possível.
Por exemplo, você pode enviar várias solicitações pressionando o botão (filtrando toque duplo), aguardar que cada uma responda e combinar a resposta com o que o usuário inseriu na tela, executar outra solicitação e ir para a próxima tela, transferindo o resultado para ela, quando esse Rx tornará possível manipular sucintamente os erros e concluir essa cadeia (cancelar solicitações) ao sair da tela, e toda essa lógica precisará de duas dúzias de linhas de código digitado:
Vale a pena falar sobre a única propriedade do disposeBag. Como pode ser visto nas capturas de tela, cada assinatura é colocada nela, e isso é necessário para controlar sua vida útil. Ou seja, as assinaturas são válidas enquanto a "bolsa" permanecer em que foram colocadas, nesse caso, enquanto o controlador permanecer.Além da compactação, é difícil cometer um erro no código acima, pois cada fechamento retorna algo e não contém efeitos colaterais. Este é o poder que estávamos procurando.
Você pode notar mais um ponto importante: como a classe não possui propriedades, não é necessário escrever [self fraco], o que afeta positivamente a legibilidade do código. Todas as funções podem e podem ser melhor definidas localmente no método em que são usadas ou executadas em classes separadas. A propósito, links para dependências (ViewModel, Presenter, etc.) nesse caso podem ser passados como um argumento para o método do controlador; nesse caso, não há necessidade de salvá-los nas propriedades. Isto está correto.
Depois de revisar, é hora de usar o Observable para simplificar o desenvolvimento. Como exatamente? Vamos voltar à idéia de métodos "limpos" e tentar implementar a lógica de uma tela pequena em seu único método.Para maior clareza, escolheremos o método de finalizar o carregamento da visualização (viewDidLoad). Naturalmente, se a tela for composta no IB, teremos que criar propriedades para pontos de venda, mas isso não é assustador, porque os elementos em si não representam a lógica de negócios, portanto, não afetarão muito a complexidade da tela. Mas se a tela é composta de código, você pode ficar sem propriedades (exceto para o disposeBag), criando elementos em nosso método e usando-os ali mesmo. E a imperatividade dos elementos UIKit que foram descritos anteriormente? O Rx, além da própria abordagem, fornece wrappers reativos para componentes padrão da interface do usuário; portanto, na maioria dos casos, você pode obter a sequência necessária de eventos no local. Ou, por outro lado, vincule o Observable existente a, por exemplo, uma tabela - traga uma solicitação a ele para que ele atualize seu conteúdo imediatamente após sua conclusão:
A ligação a coleções é bastante flexível, mas, por padrão, funciona apenas através de reloadData. Para atualizações de pontos, existe uma maravilhosa biblioteca depurada do mesmo autor - RxDataSources. Com isso, você pode esquecer as falhas durante o batchUpdates.O que acontecerá depois? Um método de tela única crescerá e, em um caso bastante complicado, ficará difícil de manter. E quando isso acontece, fica subitamente claro que esse método não depende de nada além de si mesmo, e a abordagem reativa dividiu o código em blocos lógicos que podem ser facilmente colocados em objetos separados, projetando-os de maneira semelhante. Mas desta vez, os métodos já terão algumas dependências na tela e retornarão algum resultado. O ponto positivo é que a assinatura, neste caso, é obtida com o maior conteúdo possível; pode-se observar que a função requer seu trabalho e qual é o seu resultado. Pode ser algo como isto:

Estruturas separadas ajudam a manter o cabeçalho do método legível e organizado, pois pode haver muitas dependências.
É importante entender que o método não precisa ser o único em todo o objeto, a essência de sua independência um do outro. Graças ao Rx, suas entradas e saídas podem ser assíncronas e representam um ou mais Observáveis, fornecendo outra dimensão para manipulação de dados.
Essa abordagem desata suas mãos e permite implementar telas de praticamente qualquer complexidade, mantendo o código explícito e com pouca flexibilidade.