Iara  Simões

Iara Simões

1657962660

Entendendo Os Padrões De Projeto Em Kotlin

Se você está aqui, provavelmente já ouviu falar de Kotlin antes. Neste artigo, revisaremos o que é Kotlin, por que é útil e quais são os padrões de design.

Em seguida, veremos os padrões de design Kotlin mais importantes e amplamente usados ​​(como o padrão de provedor e muitos outros), acompanhados de trechos de código e exemplos de casos de uso.

Java quem? Como o Kotlin fornece uma maneira mais fácil de codificar

O pessoal da JetBrains criou a linguagem de programação Kotlin para facilitar suas vidas. Eles estavam usando Java, e a linguagem estava reduzindo sua produtividade na época. A solução foi Kotlin, uma linguagem de programação de código aberto e estaticamente tipada.

Kotlin é uma linguagem de programação orientada a objetos que também faz uso de programação funcional, reduzindo drasticamente a quantidade de código clichê. Ele ajuda a aumentar a produtividade dos programadores e fornece uma maneira moderna de desenvolver aplicativos nativos.

Hoje em dia, Kotlin é amplamente preferido sobre Java para desenvolvimento Android.

Padrões de design Kotlin: nem todos os heróis usam capas

O desenvolvimento de software envolve muita criatividade e pensamento fora da caixa, já que muitas vezes você se depara com problemas complexos que exigem soluções exclusivas. Mas reinventar a roda toda vez que você está tentando resolver algo não é viável – e não é necessário.

Na área de design de software, muitas vezes você enfrentará situações que outros já enfrentaram antes. Felizmente, agora temos algumas soluções reutilizáveis ​​para esses problemas recorrentes.

Esses paradigmas já foram usados ​​e testados antes. Tudo o que resta a fazer é entender o problema claramente para identificar um padrão de projeto adequado que atenda às suas necessidades.

Tipos de padrões de design Kotlin

Existem três tipos principais de padrões de projeto: criacional, estrutural e comportamental. Cada uma dessas categorias responde a uma pergunta diferente sobre nossas aulas e como as usamos.

 

Nas próximas seções, abordaremos cada padrão de design em profundidade e entenderemos como usar os recursos do Kotlin para implementar esses padrões. Também revisaremos os padrões de design mais populares de cada categoria: vamos defini-los, cobrir alguns exemplos de casos de uso e examinar alguns códigos.

O que são padrões de design criacional?

Como o nome sugere, os padrões dessa categoria se concentram em como os objetos estão sendo criados. Usar esses padrões garantirá que nosso código seja flexível e reutilizável.

Existem vários padrões de design de criação em Kotlin, mas nesta seção, vamos nos concentrar em cobrir o método de fábrica, o método de fábrica abstrato (você também pode consultar o modelo de provedor da Microsoft ), o padrão singleton e o padrão de construtor.

Método de fábrica e de fábrica abstrata (modelo de provedor)

Esses dois padrões de criação têm algumas semelhanças, mas são implementados de forma diferente e têm casos de uso distintos.

O padrão de método de fábrica é baseado na definição de métodos abstratos para as etapas de inicialização de uma superclasse. Esse padrão de projeto Kotlin permite que as subclasses individuais definam a maneira como são inicializadas e criadas.

Em comparação com outros padrões de criação, como o padrão do construtor, o padrão de método de fábrica não requer escrever uma classe separada, mas apenas um ou mais métodos adicionais contendo a lógica de inicialização.

Um exemplo claro desse padrão é a conhecida RecyclerView.Adapterclasse do Android, especificamente seu onCreateViewHolder()método. Este método instancia um novo ViewHolderbaseado no conteúdo do elemento de lista específico, conforme demonstrado abaixo:

class MyRecyclerViewAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
   override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
       return when (viewType) {
           0 -> HeaderViewHolder()
           1 -> SeparatorViewHolder()
           else -> ContentViewHolder()
       }
   }
}

Neste exemplo, o onCreateViewHolder()método é definido na RecyclerView.Adapter superclasse, que por sua vez é usada pelo
código interno do adaptador.

Ao tornar o método abstrato na superclasse, o adaptador permite que suas subclasses de implementação definam a lógica de inicialização para seus ViewHolders com base em suas necessidades.

Por outro lado, o método abstract factory em Kotlin — que, como o modelo de provedor da Microsoft, usa uma ProviderBaseclasse — depende da criação de uma interface que permite que uma família de classes intimamente relacionadas seja instanciada.

Um exemplo seria uma fábrica que fabrica peças de automóveis para diferentes fabricantes:

abstract class CarPartsFactory { 
    fun buildEngine(/* engine parts */): Engine
    fun buildChassis(/* chassis materials */): Chassis
}

class CarPartsFactoryProvider { 
  inline fun <reified M : Manufacturer> createFactory(): CarPartsFactory { 
    return when (M::class) { 
        Manufacturer.Audi -> AudiPartsFactory()
        Manufacturer.Toyota -> ToyotaPartsFactory()
        }
    }
}
class AudiPartsFactory: CarPartsFactory() { 
    override fun buildEngine(...): AudiEngine
    override fun buildChassis(...): AudiChassis
}

class ToyotaPartsFactory: CarPartsFactory() { 
    override fun buildEngine(...): ToyotaEngine
    override fun buildChassis(...): ToyotaChassis
}

// Usage:
val factoryProvider = CarPartsFactoryProvider()
val partsFactory = factoryProvider.createFactory<Manufacturer.Toyota>()
val engine = partsFactory.buildEngine()

Neste exemplo, a interface funciona como um modelo para que tipo de peças de carro as fábricas independentes devem construir e de quais materiais (argumentos). Essas fábricas (subclasses) construirão as peças com base nos requisitos e processos específicos desses fabricantes.

Padrão de design singleton

O padrão singleton é provavelmente um dos padrões de projeto mais conhecidos. A maioria dos desenvolvedores que trabalham com POO já encontraram esse padrão de design antes. No entanto, também é um dos padrões mais mal utilizados e incompreendidos por aí. Discutiremos o porquê com mais detalhes no final desta seção.

Esse padrão de design permite que o desenvolvedor defina uma classe que será instanciada apenas uma vez em todo o projeto. Cada local em que é usado fará uso da mesma instância, reduzindo assim o uso de memória e garantindo a consistência.

Quando precisaríamos usar o padrão de projeto singleton?

Geralmente, usaríamos o padrão de design singleton ao lidar com aplicativos multithread (por exemplo, log, armazenamento em cache), quando é importante garantir uma única fonte confiável de verdade.

Por definição, uma Singletonclasse só será instanciada uma vez, seja de forma ansiosa (quando a classe é carregada) ou preguiçosamente (no momento em que é acessada pela primeira vez).

O exemplo a seguir mostra uma das abordagens mais simples para definir uma Singletonclasse em Java. Estamos olhando para Java porque é um pouco mais expressivo que Kotlin, o que nos permite entender os conceitos que fazem o padrão funcionar como funciona:

// Java implementation
public class Singleton { 
     private static Singleton instance = null;

     private Singleton() { 
   // Initialization code
     }
     public static Singleton getInstance() { 
         if (instance == null) { 
           instance = Singleton()
         }
    
   return instance;
     }
     // Class implementation...
}

Observe que o privateconstrutor e o getInstance()método estático garantem que a classe seja diretamente responsável por quando é instanciada e como é acessada.

Agora, vamos ver como implementar o padrão singleton em Kotlin:

// Kotlin implementation
object Singleton { 
    // class implementation
}

No Kotlin, a implementação de um Singletonobjeto é um recurso incluído diretamente que é seguro para threads. O padrão também é instanciado no momento do primeiro acesso, de forma semelhante à implementação Java acima.

Você pode ler mais sobre expressões e declarações de objetos Kotlin nos documentos oficiais. Observe que, embora esse recurso pareça semelhante aos objetos complementares no Kotlin, os objetos complementares podem ser considerados mais como a definição de campos e métodos estáticos no Kotlin.

Singletons podem ter muitos casos de uso, incluindo:

  • Mantendo um estado global dentro do sistema
  • Implementação de outros padrões de design, como fachadas ou padrões de fábrica diferentes
  • Fornecendo uma maneira direta de acessar dados globais

Por que o padrão singleton é considerado amplamente mal utilizado?

Não só o padrão singleton é amplamente mal utilizado e mal compreendido, mas em várias ocasiões, tem sido chamado de antipadrão. A razão para isso é que a maioria de seus benefícios também pode ser considerada suas maiores desvantagens.

Por exemplo, esse padrão de design torna muito fácil adicionar um estado global aos aplicativos, o que é notoriamente difícil de manter se a base de código aumentar de tamanho.

Além disso, o fato de poder ser acessado em qualquer lugar, a qualquer hora, torna muito mais difícil entender a hierarquia de dependências do sistema. Simplesmente mover algumas classes ou trocar uma implementação por outra pode se tornar um problema sério se houver muitas dependências em métodos e campos singleton.

Qualquer pessoa que tenha trabalhado com bases de código com muitos componentes globais ou estáticos entende por que isso é um problema.

Em seguida, como a Singletonclasse é diretamente responsável por criar, manter e expor seu estado único, isso quebra o Princípio de Responsabilidade Única .

Finalmente, como só pode haver uma instância de objeto de cada Singletonclasse durante o tempo de execução, o teste também se torna mais difícil. No momento em que uma classe diferente depende de um campo ou método do Singleton, os dois componentes serão fortemente acoplados.

Como os Singletonmétodos não podem ser (facilmente) zombados ou trocados por implementações falsas, o teste de unidade da classe dependente em isolamento completo não é possível.

Embora possa parecer atraente definir cada componente de alto nível como um singleton, isso é altamente desencorajado. Às vezes, os benefícios podem superar as desvantagens, portanto, não devemos abusar desse padrão e você deve sempre garantir que você e sua equipe entendam as implicações.

Padrão de design do construtor

O padrão do construtor é especialmente útil para classes que requerem lógica de inicialização complexa ou que precisam ser altamente configuráveis ​​em termos de seus parâmetros de entrada.

Por exemplo, se fôssemos implementar uma Carclasse que pegasse todas as diferentes partes que compõem um carro como argumentos do construtor, a lista de argumentos para esse construtor poderia ser bastante longa. Ter certos argumentos que são opcionais, enquanto outros são obrigatórios, apenas aumenta a complexidade.

Em Java, isso significaria escrever um método construtor separado para cada combinação possível de argumentos de entrada, sem mencionar a lógica de inicialização separada e tediosa se o carro também precisasse ser montado durante a instanciação. É aqui que os padrões do construtor vêm em socorro.

Ao usar um padrão de projeto do construtor, devemos definir uma Builderclasse para a classe em questão (no nosso exemplo, Car), que será responsável por receber todos os argumentos separadamente e por realmente instanciar o objeto resultante:

class Car(val engine: Engine, val turbine: Turbine?, val wheels: List<Wheel>, ...) {

   class Builder {
       private lateinit var engine: Engine
       private var turbine: Turbine? = null
       private val wheels: MutableList<Wheel> = mutableListOf()
       fun setEngine(engine: Engine): Builder {
           this.engine = engine
           return this
       }
       fun setTurbine(turbine: Turbine): Builder {
           this.turbine = turbine
           return this
       }
       fun addWheels(wheels: List<Wheel>): Builder {
           this.wheels.addAll(wheels)
           return this
       }
       fun build(): Car {
           require(engine.isInitialized) { "The car needs an engine" }
           require(wheels.size < 4) { "The car needs at least 4 wheels" }
           return Car(engine, turbine, wheels)
       }
   }
}

Como você pode ver, o Builderé diretamente responsável por lidar com a lógica de inicialização enquanto também lida com as diferentes configurações (argumentos opcionais e obrigatórios) e pré-condições (número mínimo de rodas).

Observe que todos os métodos de configuração retornam a instância atual do construtor. Isso é feito para permitir a criação fácil de objetos encadeando esses métodos da
seguinte maneira:

val car = Car.Builder()
    .setEngine(...)
    .setTurbine(...)
    .addWheels(...)
    .build()

Embora o padrão de design do construtor seja uma ferramenta muito poderosa para linguagens como Java, vemos que ele é usado com muito menos frequência em Kotlin porque já existem argumentos opcionais como um recurso de linguagem. Dessa forma, as configurações e a lógica de inicialização podem ser tratadas diretamente com um construtor, assim:

data class Car(val engine: Engine, val turbine: Turbine? = null, val wheels: List<Wheel>) {
    init { /* Validation logic */ }
}

Isso é bom para objetos simples como data classes, onde a complexidade vem principalmente de ter argumentos opcionais.

No caso de classes maiores com lógica mais complexa, é altamente recomendável extrair o código de inicialização em uma classe separada, como um arquivo Builder.

Isso ajuda a manter o Princípio de Responsabilidade Única, pois a classe não é mais responsável por como ela é criada e validada. Também permite que a lógica de inicialização seja testada de forma mais elegante.

O que são padrões de projeto estruturais?

Precisamos de padrões de projeto estrutural porque definiremos certas estruturas em nossos projetos. Queremos ser capazes de identificar corretamente as relações entre classes e objetos para compô-los de forma a simplificar as estruturas do projeto.

Nas seções abaixo, abordaremos o padrão de design do adaptador e o padrão de design do decorador em detalhes, além de revisar brevemente alguns casos de uso para o padrão de design de fachada.

Padrão de design do adaptador

O nome desse padrão sugere o trabalho que ele realiza: ele preenche a lacuna entre duas interfaces incompatíveis e permite que elas funcionem juntas, como um adaptador que você usaria para usar um carregador de telefone americano em uma tomada europeia.

No desenvolvimento de software, você normalmente usará um adaptador quando precisar converter alguns dados em um formato diferente. Por exemplo, os dados que você recebe do back-end podem ser necessários em uma representação diferente em seu repositório/para a interface do usuário.

Um adaptador também é útil quando você precisa realizar uma operação usando duas classes que não são compatíveis por padrão.

Vejamos um exemplo de um padrão de design de adaptador em Kotlin:

class Car(...)  {
fun move() { /* Implementation */ }
}
interface WaterVehicle { 
    fun swim()
}
class CarWaterAdapter(private val adaptee: Car): WaterVehicle {
    override fun swim() { 
// whatever is needed to make the car work on water
// ... might be easier to just use a boat instead
        startSwimming(adaptee)
    }
}

// Usage
val standardCar: Car = Car(...)
val waterCar: WaterVehicle = CarWaterAdapter(standardCar)
waterCar.swim()

No código acima, estamos essencialmente criando um wrapper sobre o standardCar(ou seja, o adaptee). Ao fazer isso, adaptamos sua funcionalidade a um contexto diferente com o qual seria incompatível (por exemplo, para uma corrida de água).

Padrão de design do decorador

O padrão de projeto decorador também se enquadra na categoria de padrões estruturais, pois permite atribuir novos comportamentos a uma classe existente. Novamente, fazemos isso criando um wrapper que terá os comportamentos mencionados.

Vale a pena notar aqui que o wrapper implementará apenas os métodos que o objeto encapsulado já definiu. Ao criar um wrapper que se qualifique como decorador, você adicionará alguns comportamentos à sua classe sem alterar sua estrutura de forma alguma.

class BasicCar {
    fun drive() { /* Move from A to B */ }
}

class OffroadCar : BasicCar {
    override fun drive() {
       initialiseDrivingMode()
       super.drive()
    }
    private fun initialiseDrivingMode() { 
       /* Configure offroad driving mode */ 
    }
}

No exemplo acima, começamos com um carro básico, que funciona bem na cidade e em estradas bem conservadas. No entanto, um carro como este não o ajudará muito quando você estiver em uma floresta e houver neve por toda parte.

Você não está alterando a função básica do carro (ou seja, levando passageiros do ponto A ao ponto B). Em vez disso, você está apenas aprimorando seu comportamento – em outras palavras, agora pode levá-lo do ponto A ao ponto B em uma estrada difícil, mantendo você seguro.

A chave aqui é que estamos fazendo apenas alguma lógica de configuração, após a qual ainda dependemos da lógica de condução padrão chamando super.drive().

Você pode estar pensando que isso é muito semelhante ao modo como o padrão do adaptador funcionava - e você não estaria errado. Padrões de design geralmente têm muitas semelhanças, mas quando você descobre o que cada um deles pretende alcançar e por que você usaria qualquer um deles, eles se tornam muito mais intuitivos.

Neste exemplo, a ênfase do padrão adaptador é fazer interfaces aparentemente incompatíveis (ou seja, o carro e a estrada difícil) trabalharem juntas, enquanto o decorador visa enriquecer a funcionalidade de uma classe base existente.

Padrão de design de fachada

Não vamos entrar em muitos detalhes sobre o padrão de fachada, mas, considerando o quão comum ele é, vale a pena mencioná-lo. O principal objetivo do padrão de fachada é fornecer uma interface mais simples e unificada para um conjunto de componentes relacionados.

Aqui está uma analogia útil: um carro é uma máquina complexa composta por centenas de peças diferentes que trabalham juntas. No entanto, não estamos interagindo diretamente com todas essas partes, mas apenas com algumas partes que podemos acessar do banco do motorista (por exemplo, pedais, rodas, botões) que atuam como uma interface simplificada para usarmos o carro.

Podemos aplicar o mesmo princípio no design de software.

Imagine que você está desenvolvendo um framework ou biblioteca. Normalmente, você teria dezenas, centenas ou até milhares de classes com estruturas e interações complexas.

Os consumidores dessas bibliotecas não devem se preocupar com toda essa complexidade, então você deve definir uma API simplificada com a qual eles possam usar seu trabalho. Este é o padrão de fachada em ação.

Ou imagine que uma tela precise calcular e exibir informações complexas provenientes de várias fontes de dados. Em vez de expor tudo isso à camada de UI, seria muito mais elegante definir uma fachada para juntar todas essas dependências e expor os dados no formato adequado para renderizá-los imediatamente.

O padrão de fachada seria uma boa solução para tais situações.

O que são padrões de design comportamentais?

Padrões de design comportamentais são mais focados no lado algorítmico das coisas. Eles fornecem uma maneira mais precisa para os objetos interagirem e se relacionarem uns com os outros, assim como para nós, desenvolvedores, entendermos as responsabilidades dos objetos.

Padrão de projeto do observador

O padrão de observador deve ser familiar para qualquer pessoa que tenha trabalhado com bibliotecas de estilo reativo, como RxJava, LiveData, Kotlin Flow ou qualquer biblioteca JavaScript reativa. Ele essencialmente descreve um relacionamento de comunicação entre dois objetos onde um deles está observablee o outro é o observer.

Isso é muito semelhante ao modelo produtor-consumidor, no qual o consumidor espera ativamente que a entrada seja recebida do produtor. Cada vez que um novo evento ou dado é recebido, o consumidor invocará seu método predefinido para tratar o referido evento.

val observable: Flow<Int> = flow { 
    while (true) { 
        emit(Random.nextInt(0..1000))
        delay(100)
    }
}

val observerJob = coroutineScope.launch {
observable.collect { value -> 
println("Received value $value")
    }
}

No exemplo acima, criamos um assíncrono Flowque emite um valor inteiro aleatório uma vez a cada segundo. Depois disso, coletamos isso Flowem uma corrotina separada lançada de nosso escopo atual e definimos um manipulador para cada novo evento.

Tudo o que estamos fazendo neste exemplo é imprimir os valores recebidos pelo arquivo Flow. A fonte Flowcomeçará a emitir no momento em que for coletada e continuará indefinidamente ou até que a corrotina do coletor seja cancelada.

O Kotlin Flowé baseado em corrotinas, o que o torna uma ferramenta muito poderosa para manipulação de simultaneidade e para escrever sistemas reativos, mas há muito mais a ser aprendido sobre os diferentes tipos de fluxos da documentação oficial , como fluxos quentes ou frios.

O padrão observador é uma maneira limpa e intuitiva de escrever código de forma reativa. A programação reativa tem sido a maneira de implementar interfaces de usuário modernas de aplicativos no Android via LiveData. Mais recentemente, ganhou popularidade com a adição da família de bibliotecas Rx e a introdução de fluxos assíncronos.

Padrão de design de estratégia

O padrão de estratégia é útil quando temos uma família de algoritmos ou soluções relacionadas que devem ser intercambiáveis ​​e precisamos decidir qual usar em tempo de execução.

Podemos fazer isso abstraindo os algoritmos por meio de uma interface que define a assinatura do algoritmo e permitindo que as implementações reais determinem o algoritmo usado.

Por exemplo, imagine que temos várias etapas de validação para um objeto que contém entrada do usuário. Para este caso de uso, podemos definir e aplicar a seguinte interface:

fun interface ValidationRule { 
fun validate(input: UserInput): Boolean
}
class EmailValidation: ValidationRule { 
override fun validate(input: UserInput) =  validateEmail(input.emailAddress)
}

val emailValidation = EmailValidation()
val dateValidation: ValidationRule = { userInput -> validateDate(userInput.date) }

val userInput = getUserInput()
val validationRules: List<ValidationRule> = listOf(emailValidation, dateValidation)
val isValidInput = validationRules.all { rule -> rule.validate(userInput) }

Durante o tempo de execução, cada um dos algoritmos das Regras de Validação que adicionamos à lista será executado. isValidInputsó será verdade se todos os validate()métodos tiverem sido bem sucedidos.

Anote a funpalavra-chave da definição da interface. Isso informa ao compilador que a interface a seguir é funcional, o que significa que ela tem um e apenas um método, permitindo implementá-la convenientemente por meio de funções anônimas, como fizemos para a dateValidationregra.

Conclusão

Neste artigo, examinamos os tipos de padrões de design Kotlin e entendemos quando e como usar alguns dos padrões de design mais usados ​​em Kotlin. Espero que o post tenha esclarecido esse tópico para você e mostrado como aplicar esses padrões em seus projetos.

Quais padrões você costuma usar em seus projetos e em quais situações? Existem outros padrões de design Kotlin sobre os quais você gostaria de saber mais? Se você tiver alguma dúvida, não tenha medo de entrar em contato na seção de comentários ou no meu blog Medium . Obrigado por ler.

Fonte: https://blog.logrocket.com/understanding-kotlin-design-patterns/

#kotlin 

What is GEEK

Buddha Community

Entendendo Os Padrões De Projeto Em Kotlin

Juned Ghanchi

1621508255

Kotlin App Development Company in India, Hire Kotlin Developers

We are a prime Kotlin app developer in India. We build and provide the best personalized Android apps, migration services, ongoing maintenance, and management.

We have the most efficient Kotlin developers that build ultramodern, interactive, and secure mobile apps. The technologies we use to create the most advanced Kotlin apps are AR/VR, AI/ML, IoT, etc.

Hire Kotlin app developers in India. Meet us, and we will help you meet all of your technology requirements.

#kotlin app development company india #hire kotlin developers india #kotlin app development company #hire kotlin developers #kotlin development agency #kotlin app programmers

Iara  Simões

Iara Simões

1657962660

Entendendo Os Padrões De Projeto Em Kotlin

Se você está aqui, provavelmente já ouviu falar de Kotlin antes. Neste artigo, revisaremos o que é Kotlin, por que é útil e quais são os padrões de design.

Em seguida, veremos os padrões de design Kotlin mais importantes e amplamente usados ​​(como o padrão de provedor e muitos outros), acompanhados de trechos de código e exemplos de casos de uso.

Java quem? Como o Kotlin fornece uma maneira mais fácil de codificar

O pessoal da JetBrains criou a linguagem de programação Kotlin para facilitar suas vidas. Eles estavam usando Java, e a linguagem estava reduzindo sua produtividade na época. A solução foi Kotlin, uma linguagem de programação de código aberto e estaticamente tipada.

Kotlin é uma linguagem de programação orientada a objetos que também faz uso de programação funcional, reduzindo drasticamente a quantidade de código clichê. Ele ajuda a aumentar a produtividade dos programadores e fornece uma maneira moderna de desenvolver aplicativos nativos.

Hoje em dia, Kotlin é amplamente preferido sobre Java para desenvolvimento Android.

Padrões de design Kotlin: nem todos os heróis usam capas

O desenvolvimento de software envolve muita criatividade e pensamento fora da caixa, já que muitas vezes você se depara com problemas complexos que exigem soluções exclusivas. Mas reinventar a roda toda vez que você está tentando resolver algo não é viável – e não é necessário.

Na área de design de software, muitas vezes você enfrentará situações que outros já enfrentaram antes. Felizmente, agora temos algumas soluções reutilizáveis ​​para esses problemas recorrentes.

Esses paradigmas já foram usados ​​e testados antes. Tudo o que resta a fazer é entender o problema claramente para identificar um padrão de projeto adequado que atenda às suas necessidades.

Tipos de padrões de design Kotlin

Existem três tipos principais de padrões de projeto: criacional, estrutural e comportamental. Cada uma dessas categorias responde a uma pergunta diferente sobre nossas aulas e como as usamos.

 

Nas próximas seções, abordaremos cada padrão de design em profundidade e entenderemos como usar os recursos do Kotlin para implementar esses padrões. Também revisaremos os padrões de design mais populares de cada categoria: vamos defini-los, cobrir alguns exemplos de casos de uso e examinar alguns códigos.

O que são padrões de design criacional?

Como o nome sugere, os padrões dessa categoria se concentram em como os objetos estão sendo criados. Usar esses padrões garantirá que nosso código seja flexível e reutilizável.

Existem vários padrões de design de criação em Kotlin, mas nesta seção, vamos nos concentrar em cobrir o método de fábrica, o método de fábrica abstrato (você também pode consultar o modelo de provedor da Microsoft ), o padrão singleton e o padrão de construtor.

Método de fábrica e de fábrica abstrata (modelo de provedor)

Esses dois padrões de criação têm algumas semelhanças, mas são implementados de forma diferente e têm casos de uso distintos.

O padrão de método de fábrica é baseado na definição de métodos abstratos para as etapas de inicialização de uma superclasse. Esse padrão de projeto Kotlin permite que as subclasses individuais definam a maneira como são inicializadas e criadas.

Em comparação com outros padrões de criação, como o padrão do construtor, o padrão de método de fábrica não requer escrever uma classe separada, mas apenas um ou mais métodos adicionais contendo a lógica de inicialização.

Um exemplo claro desse padrão é a conhecida RecyclerView.Adapterclasse do Android, especificamente seu onCreateViewHolder()método. Este método instancia um novo ViewHolderbaseado no conteúdo do elemento de lista específico, conforme demonstrado abaixo:

class MyRecyclerViewAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
   override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
       return when (viewType) {
           0 -> HeaderViewHolder()
           1 -> SeparatorViewHolder()
           else -> ContentViewHolder()
       }
   }
}

Neste exemplo, o onCreateViewHolder()método é definido na RecyclerView.Adapter superclasse, que por sua vez é usada pelo
código interno do adaptador.

Ao tornar o método abstrato na superclasse, o adaptador permite que suas subclasses de implementação definam a lógica de inicialização para seus ViewHolders com base em suas necessidades.

Por outro lado, o método abstract factory em Kotlin — que, como o modelo de provedor da Microsoft, usa uma ProviderBaseclasse — depende da criação de uma interface que permite que uma família de classes intimamente relacionadas seja instanciada.

Um exemplo seria uma fábrica que fabrica peças de automóveis para diferentes fabricantes:

abstract class CarPartsFactory { 
    fun buildEngine(/* engine parts */): Engine
    fun buildChassis(/* chassis materials */): Chassis
}

class CarPartsFactoryProvider { 
  inline fun <reified M : Manufacturer> createFactory(): CarPartsFactory { 
    return when (M::class) { 
        Manufacturer.Audi -> AudiPartsFactory()
        Manufacturer.Toyota -> ToyotaPartsFactory()
        }
    }
}
class AudiPartsFactory: CarPartsFactory() { 
    override fun buildEngine(...): AudiEngine
    override fun buildChassis(...): AudiChassis
}

class ToyotaPartsFactory: CarPartsFactory() { 
    override fun buildEngine(...): ToyotaEngine
    override fun buildChassis(...): ToyotaChassis
}

// Usage:
val factoryProvider = CarPartsFactoryProvider()
val partsFactory = factoryProvider.createFactory<Manufacturer.Toyota>()
val engine = partsFactory.buildEngine()

Neste exemplo, a interface funciona como um modelo para que tipo de peças de carro as fábricas independentes devem construir e de quais materiais (argumentos). Essas fábricas (subclasses) construirão as peças com base nos requisitos e processos específicos desses fabricantes.

Padrão de design singleton

O padrão singleton é provavelmente um dos padrões de projeto mais conhecidos. A maioria dos desenvolvedores que trabalham com POO já encontraram esse padrão de design antes. No entanto, também é um dos padrões mais mal utilizados e incompreendidos por aí. Discutiremos o porquê com mais detalhes no final desta seção.

Esse padrão de design permite que o desenvolvedor defina uma classe que será instanciada apenas uma vez em todo o projeto. Cada local em que é usado fará uso da mesma instância, reduzindo assim o uso de memória e garantindo a consistência.

Quando precisaríamos usar o padrão de projeto singleton?

Geralmente, usaríamos o padrão de design singleton ao lidar com aplicativos multithread (por exemplo, log, armazenamento em cache), quando é importante garantir uma única fonte confiável de verdade.

Por definição, uma Singletonclasse só será instanciada uma vez, seja de forma ansiosa (quando a classe é carregada) ou preguiçosamente (no momento em que é acessada pela primeira vez).

O exemplo a seguir mostra uma das abordagens mais simples para definir uma Singletonclasse em Java. Estamos olhando para Java porque é um pouco mais expressivo que Kotlin, o que nos permite entender os conceitos que fazem o padrão funcionar como funciona:

// Java implementation
public class Singleton { 
     private static Singleton instance = null;

     private Singleton() { 
   // Initialization code
     }
     public static Singleton getInstance() { 
         if (instance == null) { 
           instance = Singleton()
         }
    
   return instance;
     }
     // Class implementation...
}

Observe que o privateconstrutor e o getInstance()método estático garantem que a classe seja diretamente responsável por quando é instanciada e como é acessada.

Agora, vamos ver como implementar o padrão singleton em Kotlin:

// Kotlin implementation
object Singleton { 
    // class implementation
}

No Kotlin, a implementação de um Singletonobjeto é um recurso incluído diretamente que é seguro para threads. O padrão também é instanciado no momento do primeiro acesso, de forma semelhante à implementação Java acima.

Você pode ler mais sobre expressões e declarações de objetos Kotlin nos documentos oficiais. Observe que, embora esse recurso pareça semelhante aos objetos complementares no Kotlin, os objetos complementares podem ser considerados mais como a definição de campos e métodos estáticos no Kotlin.

Singletons podem ter muitos casos de uso, incluindo:

  • Mantendo um estado global dentro do sistema
  • Implementação de outros padrões de design, como fachadas ou padrões de fábrica diferentes
  • Fornecendo uma maneira direta de acessar dados globais

Por que o padrão singleton é considerado amplamente mal utilizado?

Não só o padrão singleton é amplamente mal utilizado e mal compreendido, mas em várias ocasiões, tem sido chamado de antipadrão. A razão para isso é que a maioria de seus benefícios também pode ser considerada suas maiores desvantagens.

Por exemplo, esse padrão de design torna muito fácil adicionar um estado global aos aplicativos, o que é notoriamente difícil de manter se a base de código aumentar de tamanho.

Além disso, o fato de poder ser acessado em qualquer lugar, a qualquer hora, torna muito mais difícil entender a hierarquia de dependências do sistema. Simplesmente mover algumas classes ou trocar uma implementação por outra pode se tornar um problema sério se houver muitas dependências em métodos e campos singleton.

Qualquer pessoa que tenha trabalhado com bases de código com muitos componentes globais ou estáticos entende por que isso é um problema.

Em seguida, como a Singletonclasse é diretamente responsável por criar, manter e expor seu estado único, isso quebra o Princípio de Responsabilidade Única .

Finalmente, como só pode haver uma instância de objeto de cada Singletonclasse durante o tempo de execução, o teste também se torna mais difícil. No momento em que uma classe diferente depende de um campo ou método do Singleton, os dois componentes serão fortemente acoplados.

Como os Singletonmétodos não podem ser (facilmente) zombados ou trocados por implementações falsas, o teste de unidade da classe dependente em isolamento completo não é possível.

Embora possa parecer atraente definir cada componente de alto nível como um singleton, isso é altamente desencorajado. Às vezes, os benefícios podem superar as desvantagens, portanto, não devemos abusar desse padrão e você deve sempre garantir que você e sua equipe entendam as implicações.

Padrão de design do construtor

O padrão do construtor é especialmente útil para classes que requerem lógica de inicialização complexa ou que precisam ser altamente configuráveis ​​em termos de seus parâmetros de entrada.

Por exemplo, se fôssemos implementar uma Carclasse que pegasse todas as diferentes partes que compõem um carro como argumentos do construtor, a lista de argumentos para esse construtor poderia ser bastante longa. Ter certos argumentos que são opcionais, enquanto outros são obrigatórios, apenas aumenta a complexidade.

Em Java, isso significaria escrever um método construtor separado para cada combinação possível de argumentos de entrada, sem mencionar a lógica de inicialização separada e tediosa se o carro também precisasse ser montado durante a instanciação. É aqui que os padrões do construtor vêm em socorro.

Ao usar um padrão de projeto do construtor, devemos definir uma Builderclasse para a classe em questão (no nosso exemplo, Car), que será responsável por receber todos os argumentos separadamente e por realmente instanciar o objeto resultante:

class Car(val engine: Engine, val turbine: Turbine?, val wheels: List<Wheel>, ...) {

   class Builder {
       private lateinit var engine: Engine
       private var turbine: Turbine? = null
       private val wheels: MutableList<Wheel> = mutableListOf()
       fun setEngine(engine: Engine): Builder {
           this.engine = engine
           return this
       }
       fun setTurbine(turbine: Turbine): Builder {
           this.turbine = turbine
           return this
       }
       fun addWheels(wheels: List<Wheel>): Builder {
           this.wheels.addAll(wheels)
           return this
       }
       fun build(): Car {
           require(engine.isInitialized) { "The car needs an engine" }
           require(wheels.size < 4) { "The car needs at least 4 wheels" }
           return Car(engine, turbine, wheels)
       }
   }
}

Como você pode ver, o Builderé diretamente responsável por lidar com a lógica de inicialização enquanto também lida com as diferentes configurações (argumentos opcionais e obrigatórios) e pré-condições (número mínimo de rodas).

Observe que todos os métodos de configuração retornam a instância atual do construtor. Isso é feito para permitir a criação fácil de objetos encadeando esses métodos da
seguinte maneira:

val car = Car.Builder()
    .setEngine(...)
    .setTurbine(...)
    .addWheels(...)
    .build()

Embora o padrão de design do construtor seja uma ferramenta muito poderosa para linguagens como Java, vemos que ele é usado com muito menos frequência em Kotlin porque já existem argumentos opcionais como um recurso de linguagem. Dessa forma, as configurações e a lógica de inicialização podem ser tratadas diretamente com um construtor, assim:

data class Car(val engine: Engine, val turbine: Turbine? = null, val wheels: List<Wheel>) {
    init { /* Validation logic */ }
}

Isso é bom para objetos simples como data classes, onde a complexidade vem principalmente de ter argumentos opcionais.

No caso de classes maiores com lógica mais complexa, é altamente recomendável extrair o código de inicialização em uma classe separada, como um arquivo Builder.

Isso ajuda a manter o Princípio de Responsabilidade Única, pois a classe não é mais responsável por como ela é criada e validada. Também permite que a lógica de inicialização seja testada de forma mais elegante.

O que são padrões de projeto estruturais?

Precisamos de padrões de projeto estrutural porque definiremos certas estruturas em nossos projetos. Queremos ser capazes de identificar corretamente as relações entre classes e objetos para compô-los de forma a simplificar as estruturas do projeto.

Nas seções abaixo, abordaremos o padrão de design do adaptador e o padrão de design do decorador em detalhes, além de revisar brevemente alguns casos de uso para o padrão de design de fachada.

Padrão de design do adaptador

O nome desse padrão sugere o trabalho que ele realiza: ele preenche a lacuna entre duas interfaces incompatíveis e permite que elas funcionem juntas, como um adaptador que você usaria para usar um carregador de telefone americano em uma tomada europeia.

No desenvolvimento de software, você normalmente usará um adaptador quando precisar converter alguns dados em um formato diferente. Por exemplo, os dados que você recebe do back-end podem ser necessários em uma representação diferente em seu repositório/para a interface do usuário.

Um adaptador também é útil quando você precisa realizar uma operação usando duas classes que não são compatíveis por padrão.

Vejamos um exemplo de um padrão de design de adaptador em Kotlin:

class Car(...)  {
fun move() { /* Implementation */ }
}
interface WaterVehicle { 
    fun swim()
}
class CarWaterAdapter(private val adaptee: Car): WaterVehicle {
    override fun swim() { 
// whatever is needed to make the car work on water
// ... might be easier to just use a boat instead
        startSwimming(adaptee)
    }
}

// Usage
val standardCar: Car = Car(...)
val waterCar: WaterVehicle = CarWaterAdapter(standardCar)
waterCar.swim()

No código acima, estamos essencialmente criando um wrapper sobre o standardCar(ou seja, o adaptee). Ao fazer isso, adaptamos sua funcionalidade a um contexto diferente com o qual seria incompatível (por exemplo, para uma corrida de água).

Padrão de design do decorador

O padrão de projeto decorador também se enquadra na categoria de padrões estruturais, pois permite atribuir novos comportamentos a uma classe existente. Novamente, fazemos isso criando um wrapper que terá os comportamentos mencionados.

Vale a pena notar aqui que o wrapper implementará apenas os métodos que o objeto encapsulado já definiu. Ao criar um wrapper que se qualifique como decorador, você adicionará alguns comportamentos à sua classe sem alterar sua estrutura de forma alguma.

class BasicCar {
    fun drive() { /* Move from A to B */ }
}

class OffroadCar : BasicCar {
    override fun drive() {
       initialiseDrivingMode()
       super.drive()
    }
    private fun initialiseDrivingMode() { 
       /* Configure offroad driving mode */ 
    }
}

No exemplo acima, começamos com um carro básico, que funciona bem na cidade e em estradas bem conservadas. No entanto, um carro como este não o ajudará muito quando você estiver em uma floresta e houver neve por toda parte.

Você não está alterando a função básica do carro (ou seja, levando passageiros do ponto A ao ponto B). Em vez disso, você está apenas aprimorando seu comportamento – em outras palavras, agora pode levá-lo do ponto A ao ponto B em uma estrada difícil, mantendo você seguro.

A chave aqui é que estamos fazendo apenas alguma lógica de configuração, após a qual ainda dependemos da lógica de condução padrão chamando super.drive().

Você pode estar pensando que isso é muito semelhante ao modo como o padrão do adaptador funcionava - e você não estaria errado. Padrões de design geralmente têm muitas semelhanças, mas quando você descobre o que cada um deles pretende alcançar e por que você usaria qualquer um deles, eles se tornam muito mais intuitivos.

Neste exemplo, a ênfase do padrão adaptador é fazer interfaces aparentemente incompatíveis (ou seja, o carro e a estrada difícil) trabalharem juntas, enquanto o decorador visa enriquecer a funcionalidade de uma classe base existente.

Padrão de design de fachada

Não vamos entrar em muitos detalhes sobre o padrão de fachada, mas, considerando o quão comum ele é, vale a pena mencioná-lo. O principal objetivo do padrão de fachada é fornecer uma interface mais simples e unificada para um conjunto de componentes relacionados.

Aqui está uma analogia útil: um carro é uma máquina complexa composta por centenas de peças diferentes que trabalham juntas. No entanto, não estamos interagindo diretamente com todas essas partes, mas apenas com algumas partes que podemos acessar do banco do motorista (por exemplo, pedais, rodas, botões) que atuam como uma interface simplificada para usarmos o carro.

Podemos aplicar o mesmo princípio no design de software.

Imagine que você está desenvolvendo um framework ou biblioteca. Normalmente, você teria dezenas, centenas ou até milhares de classes com estruturas e interações complexas.

Os consumidores dessas bibliotecas não devem se preocupar com toda essa complexidade, então você deve definir uma API simplificada com a qual eles possam usar seu trabalho. Este é o padrão de fachada em ação.

Ou imagine que uma tela precise calcular e exibir informações complexas provenientes de várias fontes de dados. Em vez de expor tudo isso à camada de UI, seria muito mais elegante definir uma fachada para juntar todas essas dependências e expor os dados no formato adequado para renderizá-los imediatamente.

O padrão de fachada seria uma boa solução para tais situações.

O que são padrões de design comportamentais?

Padrões de design comportamentais são mais focados no lado algorítmico das coisas. Eles fornecem uma maneira mais precisa para os objetos interagirem e se relacionarem uns com os outros, assim como para nós, desenvolvedores, entendermos as responsabilidades dos objetos.

Padrão de projeto do observador

O padrão de observador deve ser familiar para qualquer pessoa que tenha trabalhado com bibliotecas de estilo reativo, como RxJava, LiveData, Kotlin Flow ou qualquer biblioteca JavaScript reativa. Ele essencialmente descreve um relacionamento de comunicação entre dois objetos onde um deles está observablee o outro é o observer.

Isso é muito semelhante ao modelo produtor-consumidor, no qual o consumidor espera ativamente que a entrada seja recebida do produtor. Cada vez que um novo evento ou dado é recebido, o consumidor invocará seu método predefinido para tratar o referido evento.

val observable: Flow<Int> = flow { 
    while (true) { 
        emit(Random.nextInt(0..1000))
        delay(100)
    }
}

val observerJob = coroutineScope.launch {
observable.collect { value -> 
println("Received value $value")
    }
}

No exemplo acima, criamos um assíncrono Flowque emite um valor inteiro aleatório uma vez a cada segundo. Depois disso, coletamos isso Flowem uma corrotina separada lançada de nosso escopo atual e definimos um manipulador para cada novo evento.

Tudo o que estamos fazendo neste exemplo é imprimir os valores recebidos pelo arquivo Flow. A fonte Flowcomeçará a emitir no momento em que for coletada e continuará indefinidamente ou até que a corrotina do coletor seja cancelada.

O Kotlin Flowé baseado em corrotinas, o que o torna uma ferramenta muito poderosa para manipulação de simultaneidade e para escrever sistemas reativos, mas há muito mais a ser aprendido sobre os diferentes tipos de fluxos da documentação oficial , como fluxos quentes ou frios.

O padrão observador é uma maneira limpa e intuitiva de escrever código de forma reativa. A programação reativa tem sido a maneira de implementar interfaces de usuário modernas de aplicativos no Android via LiveData. Mais recentemente, ganhou popularidade com a adição da família de bibliotecas Rx e a introdução de fluxos assíncronos.

Padrão de design de estratégia

O padrão de estratégia é útil quando temos uma família de algoritmos ou soluções relacionadas que devem ser intercambiáveis ​​e precisamos decidir qual usar em tempo de execução.

Podemos fazer isso abstraindo os algoritmos por meio de uma interface que define a assinatura do algoritmo e permitindo que as implementações reais determinem o algoritmo usado.

Por exemplo, imagine que temos várias etapas de validação para um objeto que contém entrada do usuário. Para este caso de uso, podemos definir e aplicar a seguinte interface:

fun interface ValidationRule { 
fun validate(input: UserInput): Boolean
}
class EmailValidation: ValidationRule { 
override fun validate(input: UserInput) =  validateEmail(input.emailAddress)
}

val emailValidation = EmailValidation()
val dateValidation: ValidationRule = { userInput -> validateDate(userInput.date) }

val userInput = getUserInput()
val validationRules: List<ValidationRule> = listOf(emailValidation, dateValidation)
val isValidInput = validationRules.all { rule -> rule.validate(userInput) }

Durante o tempo de execução, cada um dos algoritmos das Regras de Validação que adicionamos à lista será executado. isValidInputsó será verdade se todos os validate()métodos tiverem sido bem sucedidos.

Anote a funpalavra-chave da definição da interface. Isso informa ao compilador que a interface a seguir é funcional, o que significa que ela tem um e apenas um método, permitindo implementá-la convenientemente por meio de funções anônimas, como fizemos para a dateValidationregra.

Conclusão

Neste artigo, examinamos os tipos de padrões de design Kotlin e entendemos quando e como usar alguns dos padrões de design mais usados ​​em Kotlin. Espero que o post tenha esclarecido esse tópico para você e mostrado como aplicar esses padrões em seus projetos.

Quais padrões você costuma usar em seus projetos e em quais situações? Existem outros padrões de design Kotlin sobre os quais você gostaria de saber mais? Se você tiver alguma dúvida, não tenha medo de entrar em contato na seção de comentários ou no meu blog Medium . Obrigado por ler.

Fonte: https://blog.logrocket.com/understanding-kotlin-design-patterns/

#kotlin 

Hire Dedicated Kotlin Developer - WebClues Infotech

The most popular language used for Android app development is Kotlin and recently Kotlin also launched its Cross-Platform app development language Kotlin Multiplatform.

So are you looking to launch a mobile using Kotlin as your Programming Language?

Then you are at the right place as WebClues Infotech offers services to Hire a Dedicated Kotlin Developer who can develop an interactive and rich user-experienced mobile app. Also, WebClues has a flexible pricing model that the business can choose according to its most suitable structure.

So what are you waiting for? Hire a dedicated Kotlin developer from a reputed Web & Mobile app development agency that has successfully served more than 600+ clients.

Get in touch with us!!

Book Free Interview: https://bit.ly/3dDShFg

#hire dedicated kotlin developer #hire dedicated kotlin developer #hire kotlin developers #hire kotlin developer #hire dedicated kotlin app developer #hire kotlin app developers india

How to Develop an Android App with Kotlin?

Do you have an idea to develop an android app using Kotlin?

Are you looking for the best Kotlin app development company in the USA? We at AppClues Infotech offering versatile mobile app development services in the USA. We provide custom mobile app development solutions as per your specific business needs with the prevailing market trending technology & features.

Hire our top-notch Kotlin app designers & developers for your project at a cost-effective price.

Our Kotlin App Development Services:
• Custom Android App Development
• Kotlin AR/VR App Development
• Kotlin App UI/UX Design
• Kotlin App QA & Testing- code Optimization
• Kotlin App Migrations
• Kotlin App Maintenance and Upgrades

For more info:
Website: https://www.appcluesinfotech.com/
Email: info@appcluesinfotech.com
Call: +1-978-309-9910

#kotlin android app development company #best kotlin android app development usa #kotlin android app development #kotlin for android app development #kotlin mobile app development service #create android app with kotlin

joe biden

1617257581

Software de restauración de Exchange para restaurar sin problemas PST en Exchange Server

¿Quiere restaurar los buzones de correo de PST a Exchange Server? Entonces, estás en la página correcta. Aquí, lo guiaremos sobre cómo puede restaurar fácilmente mensajes y otros elementos de PST a MS Exchange Server.

Muchas veces, los usuarios necesitan restaurar los elementos de datos de PST en Exchange Server, pero debido a la falta de disponibilidad de una solución confiable, los usuarios no pueden obtener la solución. Háganos saber primero sobre el archivo PST y MS Exchange Server.

Conozca PST y Exchange Server

PST es un formato de archivo utilizado por MS Outlook, un cliente de correo electrónico de Windows y muy popular entre los usuarios domésticos y comerciales.

Por otro lado, Exchange Server es un poderoso servidor de correo electrónico donde todos los datos se almacenan en un archivo EDB. Los usuarios generalmente guardan la copia de seguridad de los buzones de correo de Exchange en el archivo PST, pero muchas veces, los usuarios deben restaurar los datos del archivo PST en Exchange. Para resolver este problema, estamos aquí con una solución profesional que discutiremos en la siguiente sección de esta publicación.

Un método profesional para restaurar PST a Exchange Server

No le recomendamos que elija una solución al azar para restaurar los datos de PST en Exchange Server. Por lo tanto, al realizar varias investigaciones, estamos aquí con una solución inteligente y conveniente, es decir, Exchange Restore Software. Es demasiado fácil de manejar por todos los usuarios y restaurar cómodamente todos los datos del archivo PST a Exchange Server.

Funciones principales ofrecidas por Exchange Restore Software

El software es demasiado simple de usar y se puede instalar fácilmente en todas las versiones de Windows. Con unos pocos clics, la herramienta puede restaurar los elementos del buzón de Exchange.

No es necesario que MS Outlook restaure los datos PST en Exchange. Todos los correos electrónicos, contactos, notas, calendarios, etc. se restauran desde el archivo PST a Exchange Server.

Todas las versiones de Outlook son compatibles con la herramienta, como Outlook 2019, 2016, 2013, 2010, 2007, etc. La herramienta proporciona varios filtros mediante los cuales se pueden restaurar los datos deseados desde un archivo PST a Exchange Server. El programa se puede instalar en todas las versiones de Windows como Windows 10, 8.1, 8, 7, XP, Vista, etc.

Descargue la versión de demostración del software de restauración de Exchange y analice el funcionamiento del software restaurando los primeros 50 elementos por carpeta.

Líneas finales

No existe una solución manual para restaurar los buzones de correo de Exchange desde el archivo PST. Por lo tanto, hemos explicado una solución fácil e inteligente para restaurar datos de archivos PST en Exchange Server. Simplemente puede usar este software y restaurar todos los datos de PST a Exchange Server.

Más información:- https://www.datavare.com/software/exchange-restore.html

#intercambio de software de restauración #intercambio de restauración #buzón del servidor de intercambio #herramienta de restauración de intercambio