Esta é uma nova série de artigos composta em cinco partes, uma para cada princípio do SOLID. Vou deixar o link para uma versão em inglês aqui, quando tiver uma, e vou deixar os artigos seguintes no final deste artigo quando forem feitos também.
Existem tópicos na área de desenvolvimento que são muito importantes de se aprender, e o SOLID é um deles. Às vezes encontramos situações em que não percebemos, à primeira vista, que podemos implementar esses princípios e que poderiam ser um ótimo exemplo de implementação no trabalho ou em uma entrevista de emprego.
Nesta série de artigos, vamos usar os conceitos de SOLID com Jetpack Compose e ver casos onde essa tecnologia performa muito bem com os conceitos apresentados.
O princípio que iremos focar aqui é o S - Single-responsibility principle (SRP) ou Princípio da Responsabilidade Única, que dita que uma classe (no nosso caso, um composable) deve ter apenas uma única responsabilidade, ou seja, deve fazer uma única ação ou mudança.
Em nosso exemplo, vamos analisar o caso de uma HomeScreen
:
Em nossa HomeScreen
, o designer desenvolveu alguns fluxos na tela para dar comportamentos quando interagida com o usuário. A primeira coisa que acontece é o carregamento dos conteúdos; em seguida, caso o carregamento ocorra bem, é exibida uma lista de conteúdos. O designer acrescentou também alguns elementos na tela caso o carregamento não ocorra bem ou não tenha nenhum conteúdo.
Em um desenvolvimento comum com XML
, é esperado que alguns elementos sejam criados em tela e ocultados. Durante a execução das activities
, decidimos quando eles devem aparecer. Por exemplo, se a tela tiver uma lista de conteúdos, o botão "new" deve ser pequeno e em formato de losango; quando não houver nenhum conteúdo, ele deve ser um retângulo comum e largo; caso o conteúdo esteja carregando, ele não deve aparecer.
Para esse caso, pode ser uma implementação simples, mas sabemos que em uma tela com diversos comportamentos e casos de exceção isso pode se tornar um pesadelo de condicionais e, devido à complexidade, a cada mudança no código, um novo comportamento inesperado pode ocorrer.
Implementando o Single-responsibility principle(SRP)
No nosso exemplo, iremos dividir cada comportamento de tela em componentes, uma série de funções que definirão o fluxo exibido na tela. Se você já conhece o básico de Jetpack Compose, sabe que podemos criar uma view com uma simples função e a anotação @Composable
, então não vamos abordar isso aqui.
Vamos definir cada responsabilidade por:
- Loading: carregando conteúdo
- Content: foi carregado uma lista de conteúdos
- Empty: foi carregado uma lista vazia
- Error: houve um erro no carregamento
A solução seria simplesmente criarmos os componentes abaixo:
@Composable
fun HomeLoadingScreen(){...}
@Composable
fun HomeContentScreen(
contentList: List<String>
onNewClick: ()-> Unit
){...}
@Composable
fun HomeEmptyScreen(
onNewClick: ()-> Unit
){...}
@Composable
fun HomeErrorScreen(
onOkClick: ()-> Unit
){...}
FIM Brincadeira pessoal, apesar do uso do SRP já estar exposto, vamos dar um sentido para essa separação de responsabilidade, afinal, ainda não sabemos como pode ser útil. Primeiro precisamos entender que existe uma função HomeScreen
e que essas funções de comportamento serão renderizadas nela. E isso vai depender de uma mudança de estado. Além da HomeScreen
, temos a HomeViewModel
que vai criar nossa variável MutableLiveData
que vai ficar mutando durante o uso do app.
O tipo do nosso MutableLiveData
será uma classe selada HomeUIState
que terá classes e objetos filhos representando cada comportamento (ou responsabilidade).
@Composable
fun HomeScreen(viewModel: HomeViewModel){
val state = viewModel.uiState.colectAsState()
}
class HomeViewModel(): ViewModel() {
var uiState: MutableLiveData<HomeUIState>()
...
sealed class HomeUIState() {
object Loading: HomeUIState()
data class Content (val contentList: List<String>): HomeUIState()
object Empty: HomeUIState()
object Error: HomeUIState()
}
}
Agora, vamos criar algumas funções que atualizem nosso estado uiState
(o MutableLiveData
). Funções que representam momentos como o carregamento de uma API
, ou uma coleta no sharedPreferences
.
class HomeViewModel(): ViewModel() {
private fun onInit() {
uiState.postValue(Loading)
}
private fun onGetListSuccess(list: List<String>) {
//verify if is empty
if(list.isEmpty()){
uiState.postValue(Empty)
} else {
uiState.postValue(Content(list))
}
}
private fun onGetListFailure() {
uiState.postValue(Error)
}
}
Por fim, as telas em HomeScreen
são renderizadas através de um When
:
@Composable
fun HomeScreen(viewModel: HomeViewModel){
val state = viewModel.uiState.colectAsState()
when(state) {
is Loading -> HomeLoadingScreen()
is Content -> HomeContentScreen(it.contentList){ viewModel.createNewContent()}
is Empty -> HomeEmptyScreen{ viewModel.createNewContent()}
is Error -> HomeErrorStreen{ viewModel.loadContents() }
}
}
Em resumo, definimos uma única responsabilidade para:
-
HomeLoadingScreen
: exibir os componentes correspondentes quando a tela estiver carregando os conteúdos. -
HomeContentScreen
: exibir os componentes correspondentes quando a tela estiver com a lista de conteúdos. -
HomeEmptyScreen
: exibir os componentes correspondentes quando a tela estiver com a lista vazia. -
HomeErrorScreen
: exibir os componentes correspondentes quando tiver algum erro. -
HomeScreen
: gerenciar os estados recebidos e escolher o que exibir.
No próximo artigo, iremos observar os outros princípios do SOLID usando Jetpack Compose. Vou deixar a listinha aqui abaixo, na medida que os artigos forem criados:
- Exemplificando SOLID com Jetpack Compose - parte S
- Exemplificando SOLID com Jetpack Compose - parte O
- Exemplificando SOLID com Jetpack Compose - parte L
- Exemplificando SOLID com Jetpack Compose - parte I
- Exemplificando SOLID com Jetpack Compose - parte D
Top comments (0)