Bem-vindo à aula 85 do nosso curso de PowerShell! Hoje vamos explorar um dos recursos mais poderosos e, às vezes, subutilizados do PowerShell: as classes. Se você já programou em linguagens como C# ou Java, vai se sentir em casa. Se não, não se preocupe: vamos começar do básico e avançar passo a passo.

As classes no PowerShell permitem criar tipos personalizados com propriedades e métodos, encapsulando dados e comportamento. Elas foram introduzidas na versão 5.0 e são uma forma de trazer programação orientada a objetos (POO) mais formal para seus scripts. Vamos entender como usá-las, quando usá-las e por que elas podem tornar seu código mais organizado e reutilizável.

class (PS 5+)

O PowerShell 5.0 introduziu a palavra-chave class para definir classes. Antes disso, você precisava usar Add-Type com C# ou usar PSCustomObject para simular objetos. Agora, você pode definir uma classe diretamente no seu script ou módulo.

A sintaxe básica é:

class NomeDaClasse {
    # propriedades e métodos
}

Vamos criar um exemplo simples: uma classe para representar uma Pessoa.

class Pessoa {
    [string]$Nome
    [int]$Idade
}

# Criando uma instância
$pessoa = [Pessoa]::new()
$pessoa.Nome = "João"
$pessoa.Idade = 30

# Exibindo
$pessoa

Perceba que usamos [Pessoa]::new() para criar uma nova instância. Isso chama o construtor padrão (sem parâmetros). Você também pode usar New-Object Pessoa.

As propriedades são definidas com tipos entre colchetes, como [string] e [int]. Se você não especificar um tipo, o PowerShell assume que é [object], mas é boa prática sempre definir tipos.

Além disso, você pode definir propriedades com valores padrão:

class Pessoa {
    [string]$Nome = "Sem nome"
    [int]$Idade = 0
}

E também pode usar atributos como [ValidateNotNull()] ou [ValidateRange()] para validar valores.

Uma observação importante: as classes no PowerShell são reference types, ou seja, quando você atribui uma variável a outra, ambas apontam para o mesmo objeto. Isso é diferente de tipos primitivos como int ou string.

Propriedades e métodos

Propriedades armazenam dados do objeto. Em PowerShell, você pode defini-las como campos simples (como vimos) ou com getters e setters personalizados (usando hidden e lógica interna). Métodos são funções associadas à classe que podem operar nos dados.

Vamos adicionar métodos à nossa classe Pessoa:

class Pessoa {
    [string]$Nome
    [int]$Idade

    [string]Apresentar() {
        return "Olá, meu nome é $($this.Nome) e tenho $($this.Idade) anos."
    }

    [void]Aniversario() {
        $this.Idade++
    }
}

$p = [Pessoa]::new()
$p.Nome = "Maria"
$p.Idade = 25
$p.Apresentar()
$p.Aniversario()
$p.Idade  # agora 26

Note que usamos $this para referenciar o próprio objeto dentro dos métodos. O tipo de retorno é definido antes do nome do método, como [string] ou [void]. Se você não especificar, será [object].

Você também pode definir propriedades computadas, que são calculadas dinamicamente:

class Retangulo {
    [double]$Largura
    [double]$Altura

    [double]Area() {
        return $this.Largura * $this.Altura
    }

    [double]Perimetro() {
        return 2 * ($this.Largura + $this.Altura)
    }
}

Métodos podem receber parâmetros, como qualquer função:

class Calculadora {
    [int]Somar([int]$a, [int]$b) {
        return $a + $b
    }
}

Além disso, você pode usar static para métodos que não dependem de uma instância:

class Utils {
    static [string]ConverterParaMaiusculo([string]$texto) {
        return $texto.ToUpper()
    }
}

[Utils]::ConverterParaMaiusculo("teste")

Construtores

Construtores são métodos especiais chamados quando você cria uma nova instância. Eles inicializam os valores das propriedades. Você pode definir um construtor padrão (sem parâmetros) e construtores sobrecarregados (com parâmetros diferentes).

No PowerShell, o construtor é definido com o nome NomeDaClasse(). Vamos ver:

class Pessoa {
    [string]$Nome
    [int]$Idade

    # Construtor padrão
    Pessoa() {
        $this.Nome = "Desconhecido"
        $this.Idade = 0
    }

    # Construtor com parâmetros
    Pessoa([string]$nome, [int]$idade) {
        $this.Nome = $nome
        $this.Idade = $idade
    }
}

# Uso
$p1 = [Pessoa]::new()          # usa o padrão
$p2 = [Pessoa]::new("Ana", 28) # usa o com parâmetros

Se você não definir nenhum construtor, o PowerShell cria um construtor padrão implícito que inicializa as propriedades com seus valores padrão (zero, vazio, etc.).

É possível chamar um construtor a partir de outro usando this():

class Pessoa {
    [string]$Nome
    [int]$Idade

    Pessoa() : this("Desconhecido", 0) {}

    Pessoa([string]$nome, [int]$idade) {
        $this.Nome = $nome
        $this.Idade = $idade
    }
}

Isso evita duplicação de código. Outra técnica é usar inicializadores de propriedade diretamente:

class Pessoa {
    [string]$Nome = "Desconhecido"
    [int]$Idade = 0

    Pessoa() {}
    Pessoa([string]$nome, [int]$idade) {
        $this.Nome = $nome
        $this.Idade = $idade
    }
}

Quando usar

Classes são úteis quando você precisa de objetos com comportamento e estado, especialmente se você está criando módulos ou scripts complexos. Elas ajudam a organizar código, promovem reutilização e facilitam manutenção.

Aqui estão alguns cenários onde classes são recomendadas:

  • Modelagem de entidades do mundo real: como clientes, produtos, pedidos, etc.
  • Encapsulamento de lógica: agrupar dados e funções relacionadas.
  • Herança e polimorfismo: criar hierarquias de classes para compartilhar comportamento.
  • Módulos PowerShell: exportar classes para que outros usuários possam criar objetos personalizados.

Por outro lado, para scripts simples, pode ser overkill. Se você só precisa passar dados, PSCustomObject ou hashtables podem ser suficientes. Classes adicionam uma camada de complexidade que pode não ser necessária.

Um exemplo prático: criar uma classe para gerenciar um arquivo de log.

class LogManager {
    [string]$Caminho

    LogManager([string]$caminho) {
        $this.Caminho = $caminho
    }

    [void]Escrever([string]$mensagem) {
        $linha = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - $mensagem"
        Add-Content -Path $this.Caminho -Value $linha
    }

    [string[]]Ler() {
        return Get-Content -Path $this.Caminho
    }
}

$log = [LogManager]::new("C:\temp\meuapp.log")
$log.Escrever("Início")
$log.Escrever("Processando...")
$log.Ler()

Isso encapsula a lógica de escrita e leitura, facilitando a reutilização em outros scripts.

Boas práticas e observações finais

  • Defina sempre tipos explícitos para propriedades e métodos, pois isso melhora a clareza e a performance.
  • Use hidden para propriedades que não devem ser visíveis externamente, mas ainda podem ser usadas internamente.
  • Evite lógica complexa nos construtores; prefira métodos para inicialização adicional.
  • Considere usar enum para valores fixos, como status.
  • Lembre-se de que as classes são tipadas estaticamente no PowerShell, mas você pode usar [object] para flexibilidade.

Uma dica: quando você define uma classe em um script, ela é carregada apenas quando o script é executado. Se você usar using module, as classes podem ser importadas de módulos.

Referências

Exercícios

  1. Exercício 1: Crie uma classe chamada Carro com propriedades Marca, Modelo e Ano. Adicione um método Detalhes() que retorna uma string com as informações do carro. Instancie um objeto e chame o método.
  2. ✓ Resposta:
    class Carro {
        [string]$Marca
        [string]$Modelo
        [int]$Ano
    
        [string]Detalhes() {
            return "$($this.Marca) $($this.Modelo) ($($this.Ano))"
        }
    }
    
    $carro = [Carro]::new()
    $carro.Marca = "Toyota"
    $carro.Modelo = "Corolla"
    $carro.Ano = 2020
    $carro.Detalhes()
  3. Exercício 2: Defina uma classe ContaBancaria com propriedades Titular e Saldo (privado). Inclua métodos Depositar() e Sacar() que modifiquem o saldo. Use um construtor que inicialize o titular e o saldo inicial.
  4. ✓ Resposta:
    class ContaBancaria {
        [string]$Titular
        hidden [double]$Saldo
    
        ContaBancaria([string]$titular, [double]$saldoInicial) {
            $this.Titular = $titular
            $this.Saldo = $saldoInicial
        }
    
        [void]Depositar([double]$valor) {
            $this.Saldo += $valor
        }
    
        [bool]Sacar([double]$valor) {
            if ($valor -le $this.Saldo) {
                $this.Saldo -= $valor
                return $true
            } else {
                return $false
            }
        }
    
        [double]VerSaldo() {
            return $this.Saldo
        }
    }
    
    $conta = [ContaBancaria]::new("João", 1000)
    $conta.Depositar(500)
    $conta.Sacar(200)
    $conta.VerSaldo()
  5. Exercício 3: Implemente uma classe Temperatura que armazena o valor em Celsius e tenha métodos para converter para Fahrenheit e Kelvin. Inclua um construtor que receba o valor em Celsius.
  6. ✓ Resposta:
    class Temperatura {
        [double]$Celsius
    
        Temperatura([double]$celsius) {
            $this.Celsius = $celsius
        }
    
        [double]ParaFahrenheit() {
            return ($this.Celsius * 9/5) + 32
        }
    
        [double]ParaKelvin() {
            return $this.Celsius + 273.15
        }
    }
    
    $temp = [Temperatura]::new(25)
    $temp.ParaFahrenheit()
    $temp.ParaKelvin()
  7. Exercício 4: Crie uma classe Livro com propriedades Titulo, Autor e AnoPublicacao. Adicione um método estático CriarLivro() que retorna um novo livro com valores padrão. Use esse método para criar um livro.
  8. ✓ Resposta:
    class Livro {
        [string]$Titulo
        [string]$Autor
        [int]$AnoPublicacao
    
        static [Livro]CriarLivro() {
            $livro = [Livro]::new()
            $livro.Titulo = "Sem título"
            $livro.Autor = "Desconhecido"
            $livro.AnoPublicacao = 2000
            return $livro
        }
    }
    
    $livro = [Livro]::CriarLivro()
    $livro
  9. Exercício 5: Explique quando você usaria uma classe em vez de um PSCustomObject. Dê um exemplo de situação em que uma classe é mais adequada.
  10. ✓ Resposta: Uma classe é mais adequada quando você precisa de comportamento (métodos) junto com dados, ou quando você precisa de validação, herança ou construtores. Por exemplo, se você está modelando um processo de negócio com regras, como uma transação bancária, uma classe permite encapsular a lógica de débito/crédito. PSCustomObject é bom para estruturas de dados simples, sem métodos. Classes também são melhores para reutilização em módulos e para garantir consistência nos objetos criados.