Funktsionaalne PowerShell klassidega ei ole oksüümoron, ma garanteerin seda

Tere, Habr! Esitan teile artikli tõlke "Funktsionaalne PowerShell klassidega.
Ma luban, et see ei ole oksüümoron"
autori Christopher Kuech.

Objektorienteeritud ja funktsionaalne programmeerimisparadigma võivad näida omavahel kokkusobimatud, kuid mõlemat toetatakse PowerShellis võrdselt. Peaaegu kõik programmeerimiskeeled, nii funktsionaalsed kui ka mitte, sisaldavad nime ja väärtuse sidumise arenenud võimalusi; klassid, nagu struktuurid ja kirjed, on lihtsalt üks lähenemine. Kui me piirdume klasside kasutamisel nimede ja väärtuste sidumisega ning hakkame vältima selliseid "raskete" objektorienteeritud kontseptsioonide nagu pärand, polümorfism või muutuvus (mutability) kasutamist, saame nende eeliseid kasutada, ilma et meie kood keerulisemaks muutuks. Edasi, lisades muutumatuid (immutable) tüübi konverteerimise meetodeid, saame meie funktsionaalset koodi klassidega rikastada.

Kastide maagia

Casting on Powershell is one of its most powerful features. When you cast a value, you rely on the environment's ability to add implicit initialization and validation to your application. For instance, simply casting a string to [xml] will run it through the parser code and generate a complete XML tree. We can utilize classes in our code for the same purpose.

HashTable casting

If you don't have a constructor, you can still proceed without it, using the hash table cast to your class type. Don't forget to use validation attributes to fully leverage this pattern. At the same time, we can utilize typed class properties to trigger even deeper initialization and validation logic.

class Cluster {
    [ValidatePattern("^[A-z]+$")]
    [string] $Service
    [ValidateSet("TEST", "STAGE", "CANARY", "PROD")]
    [string] $FlightingRing
    [ValidateSet("EastUS", "WestUS", "NorthEurope")]
    [string] $Region
    [ValidateRange(0, 255)]
    [int] $Index
}

[Cluster]@{
    Service       = "MyService"
    FlightingRing = "PROD"
    Region        = "EastUS"
    Index         = 2
}

Lisaks aitab kast saada puhta väljundi. Võrrelge Clusteri massiivi häshtabelite väljundit, mis edastati Format-Table'ile, sellega, mis saadakse, kui need häshtabelid enne klassiks kastida. Klasside omadused loetletakse alati selles järjekorras, milles need on määratud. Ära unusta lisada märksõna hidden kõigi nende omaduste ees, mis ei tohiks väljundis nähtavad olla.

Funktsionaalne PowerShell klassidega ei ole oksüümoron, ma garanteerin seda

Väärtuste kastimine

Kui teil on ühes argumendis konstruct, siis väärtuse kastimine teie klassi tüübiks edastab väärtuse sellele teie konstruktorile, kus saate oma klassi eksemplari initsialiseerida.

class Cluster {
    [ValidatePattern("^[A-z]+$")]
    [string] $Service
    [ValidateSet("TEST", "STAGE", "CANARY", "PROD")]
    [string] $FlightingRing
    [ValidateSet("EastUS", "WestUS", "NorthEurope")]
    [string] $Region
    [ValidateRange(0, 255)]
    [int] $Index

    Cluster([string] $id) {
        $this.Service, $this.FlightingRing, $this.Region, $this.Index = $id -split "-"
    }
}

[Cluster]"MyService-PROD-EastUS-2"

Kast stringiks

Samuti saate üle määrata klassi meetodi [string] ToString(), et määrata objekti stringi esituse loogika, näiteks kasutada stringide interpolatsiooni.

class Cluster {
    [ValidatePattern("^[A-z]+$")]
    [string] $Service
    [ValidateSet("TEST", "STAGE", "CANARY", "PROD")]
    [string] $FlightingRing
    [ValidateSet("EastUS", "WestUS", "NorthEurope")]
    [string] $Region
    [ValidateRange(0, 255)]
    [int] $Index

    [string] ToString() {
        return $this.Service, $this.FlightingRing, $this.Region, $this.Index -join "-"
    }
}

$cluster = [Cluster]@{
    Service       = "MyService"
    FlightingRing = "PROD"
    Region        = "EastUS"
    Index         = 2
}

Write-Host "We just created a model for '$cluster'"

Serialiseeritud instantside struktuur

Struktuur võimaldab turvalist deserialiseerimist. Allpool toodud näidised lõpetavad tõrke, kui andmed ei vasta meie Cluster'i spetsifikatsioonile.

# Валидация сериализованных данных

[Cluster]$cluster = Get-Content "./my-cluster.json" | ConvertFrom-Json
[Cluster[]]$clusters = Import-Csv "./my-clusters.csv"

Struktuurid teie funktsionaalses koodis

Funktsionaalsed programmid määravad esmalt andmestruktuurid, seejärel rakendavad programmi muutuste jada muutumatute andmestruktuuride üle. Vaatamata vastuolulisele muljele aitavad klassid tõeliselt funktsionaalset koodi kirjutada, pakkudes tüüpide konverteerimise meetodeid.

Kas ma kirjutan funktsionaalset Powershelli?

Paljud inimesed, kes tulevad C#-st või sarnastest taustadest, kirjutavad Powershelli, mis on C#-le sarnane. Selliselt tegutsedes loobute funktsionaalse programmeerimise kontseptsioonide kasutamisest ning saate ilmselt kasu, süvenedes objektorienteeritud programmeerimisse Powershellis või õpite paremini funktsionaalset programmeerimist.

Kui toetute tugevalt immutamatute andmete transformatsioonile, kasutades torusid (|), Where-Object, ForEach-Object, Select-Object, Group-Object, Sort-Object jne — teil on rohkem funktsionaalne stiil, ja klasid Powershellis funktsionaalsetes vormides võivad teid aidata.

Funktsionaalne klasside kasutamine

Castid, kuigi kasutavad alternatiivset süntaksit, on vaid kahe domeeni vahelised kaardistamised. Torus saab väärtuste massi kaardistada, kasutades ForEach-Object.

Allolevas näites käivitatakse Node konstruktor iga kord, kui toimub Datum'iks castimine, mis annab meile võimaluse mitte kirjutada rikast koodi. Selle tulemusena keskendub meie toru deklaratiivsele andmete pärimisele ja aggregeerimisele, samas kui meie klassid tegelevad andmete parsimise ja valideerimisega.

# Пример комбинирования классов с конвейерами для separation of concerns в конвейерах

class Node {
    [ValidateLength(3, 7)]
    [string] $Name
    [ValidateSet("INT", "PPE", "PROD")]
    [string] $FlightingRing
    [ValidateSet("EastUS", "WestUS", "NorthEurope", "WestEurope")]
    [string] $Region
    Node([string] $Name) {
        $Name -match "([a-z]+)(INT|PPE|PROD)([a-z]+)"
        $_, $this.Service, $this.FlightingRing, $this.Region = $Matches
        $this.Name = $Name
    }
}

class Datum {
    [string] $Name
    [int] $Value
    [Node] $Computer
    [int] Severity() {
        $this.Name -match "[0-9]+$"
        return $Matches[0]
    }
}

Write-Host "Urgent Security Audit Issues:"
Import-Csv "./audit-results.csv" `
    | ForEach-Object {[Datum]$_} `
    | Where-Object Value -gt 0 `
    | Group-Object {$_.Severity()} `
    | Where-Object Name -lt 2 `
    | ForEach-Object Group `
    | ForEach-Object Computer `
    | Where-Object FlightingRing -eq "PROD" `
    | Sort-Object Name, Region -Unique

Taaskasutatava klassi pakett

Mitte miski pole nii hea, nagu paistab

Kahjuks ei saa klasse eksportida moodulite kaudu samamoodi nagu funktsioone või muutujaid; kuid on mõned nipid. Oletame, et teie klassid on määratletud failis ./my-classes.ps1

  • Saate klasside faili allika:. ./my-classes.ps1. See käivitab my-classes.ps1 teie praeguses ulatuses ja määratleb seal kõik klassid failist.

  • Saate luua Powershelli mooduli, mis ekspordib kõik teie kohandatud API-d (cmdlet'id) ja seada muutujaks ScriptsToProcess = "./my-classes.ps1" oma mooduli manifestis, saavutades sama tulemuse: ./my-classes.ps1 käivitatakse teie keskkonnas.

Ükskõik, millise variandi valite, pidage meeles, et Powershelli tüübisüsteem ei suuda hallata sama nimega tüüpe, mis on laaditud erinevatest kohtadest.
Isegi kui olete laadinud kahest erinevast kohast kaks identselt klassi sama omadustega, riskite probleemidega.

Edasi liikuda

Parim viis vältida tüübi määramise probleeme on mitte lubada kasutajatel oma klasse kasutada. Selle asemel, et oodata, et kasutaja impordib klassis määratud tüübi, eksportige oma moodulist funktsioon, mis vabastab vajadusest otseselt klassi juurde pääseda. Clustere puhul saame eksportida funktsiooni New-Cluster, mis toetab kasutajasõbralikke parameetrite komplekte ja tagastab Clusteri.

class Cluster {
    [ValidatePattern("^[A-z]+$")]
    [string] $Service
    [ValidateSet("TEST", "STAGE", "CANARY", "PROD")]
    [string] $FlightingRing
    [ValidateSet("EastUS", "WestUS", "NorthEurope")]
    [string] $Region
    [ValidateRange(0, 255)]
    [int] $Index
}

function New-Cluster {
    [OutputType([Cluster])]
    Param(
        [Parameter(Mandatory, ParameterSetName = "Id", Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string] $Id,
        [Parameter(Mandatory, ParameterSetName = "Components")]
        [string] $Service,
        [Parameter(Mandatory, ParameterSetName = "Components")]
        [string] $FlightingRing,
        [Parameter(Mandatory, ParameterSetName = "Components")]
        [string] $Region,
        [Parameter(Mandatory, ParameterSetName = "Components")]
        [int] $Index
    )

    if ($Id) {
        $Service, $FlightingRing, $Region, $Index = $Id -split "-"
    }

    [Cluster]@{
        Service       = $Service
        FlightingRing = $FlightingRing
        Region        = $Region
        Index         = $Index
    }
}

Export-ModuleMember New-Cluster

Mida veel lugeda

Klassidest
Kaitsev PowerShell
Funktsionaalne programmeerimine PowerShellis

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster