Funkcinis Powershell su klasėmis nėra oksimoronas, garantuoju

Sveiki, Habr! Jūsų dėmesiui pristatau straipsnio vertimą "Funkcinis PowerShell su klasėmis.
Pažadu, tai ne oksimoronas"
pateikė Christopheris Kuechas.

Į objektą orientuotos ir funkcinės programavimo paradigmos gali atrodyti prieštaraujančios viena kitai, tačiau abi yra vienodai palaikomos „Powershell“. Beveik visos programavimo kalbos, veikiančios ar ne, turi išplėstinio vardo ir reikšmės susiejimo galimybes; Klasės, tokios kaip struktūros ir įrašai, yra tik vienas būdas. Jei apribosime klasių naudojimą pavadinimų ir reikšmių susiejimu ir vengsime sunkių objektinio programavimo koncepcijų, tokių kaip paveldėjimas, polimorfizmas ar kintamumas, galime pasinaudoti jų pranašumais neapsunkindami savo kodo. Be to, pridėdami nekintamų tipų konvertavimo metodus, galime praturtinti savo funkcinį kodą klasėmis.

Kastų magija

Kastos yra viena iš galingiausių Powershell funkcijų. Kai perduodate vertę, pasikliaujate numanomomis inicijavimo ir patvirtinimo galimybėmis, kurias aplinka prideda prie jūsų programos. Pavyzdžiui, tiesiog išleidus eilutę [xml], ji bus paleista per analizatoriaus kodą ir sugeneruos visą xml medį. Tuo pačiu tikslu galime naudoti klases savo kode.

Lieta hashtables

Jei neturite konstruktoriaus, galite tęsti be jo, perkeldami maišos lentelę į savo klasės tipą. Nepamirškite naudoti patvirtinimo atributų, kad išnaudotumėte visus šio modelio privalumus. Tuo pačiu metu galime naudoti klasės įvestas ypatybes, kad paleistume dar gilesnę inicijavimo ir patvirtinimo logiką.

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
}

Be to, liejimas padeda gauti švarų rezultatą. Palyginkite klasterio maišos lentelės masyvo, perduoto Format-Table, išvestį su tuo, ką gausite, jei pirmą kartą perkelsite šias maišos lenteles į klasę. Klasės ypatybės visada pateikiamos tokia tvarka, kokia jos ten apibrėžiamos. Nepamirškite pridėti paslėpto raktinio žodžio prieš visas tas savybes, kurių nenorite matyti rezultatuose.

Funkcinis Powershell su klasėmis nėra oksimoronas, garantuoju

Prasmių išmetimas

Jei turite konstruktorių su vienu argumentu, perdavus reikšmę klasės tipui, ji bus perduota konstruktoriui, kur galėsite inicijuoti savo klasės egzempliorių.

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"

Mesti į eilutę

Taip pat galite nepaisyti [string] ToString() klasės metodo, kad apibrėžtumėte objekto eilutės vaizdavimo logiką, pvz., naudodami eilutės interpoliaciją.

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'"

Perduokite serijinius atvejus

Cast leidžia saugiai deserializuoti. Toliau pateikti pavyzdžiai nepavyks, jei duomenys neatitiks klasterio specifikacijos

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

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

Kastos jūsų funkciniame kode

Funkcinės programos pirmiausia apibrėžia duomenų struktūras, tada įgyvendina programą kaip transformacijų seką per nekintamas duomenų struktūras. Nepaisant prieštaringo įspūdžio, klasės tikrai padeda parašyti funkcinį kodą dėl tipo konvertavimo metodų.

Ar Powershell, kurį rašau, veikia?

Daugelis žmonių iš C# ar panašių žinių rašo Powershell, kuris yra panašus į C#. Taip elgdamiesi nustojate naudoti funkcinio programavimo koncepcijas ir greičiausiai jums būtų naudinga pasinerti į objektinį programavimą Powershell arba sužinoti daugiau apie funkcinį programavimą.

Jei labai pasikliaujate nekintamų duomenų transformavimu naudodami konvejerius (|), Kur-Object, ForEach-Object, Select-Object, Group-Object, Sort-Object ir tt – turite funkcionalesnį stilių ir jums bus naudinga naudoti Powershell užsiėmimai funkciniu stiliumi.

Funkcinis klasių panaudojimas

Kastos, nors ir naudoja alternatyvią sintaksę, yra tik dviejų domenų susiejimas. Konvejerijoje galite susieti verčių masyvą naudodami ForEach-Object.

Toliau pateiktame pavyzdyje mazgo konstruktorius vykdomas kiekvieną kartą, kai nuleidžiamas taškas, ir tai suteikia mums galimybę nerašyti pakankamai daug kodo. Todėl mūsų dujotiekis daugiausia dėmesio skiria deklaratyvioms duomenų užklausoms ir kaupimui, o mūsų klasės rūpinasi duomenų analizavimu ir patvirtinimu.

# Пример комбинирования классов с конвейерами для 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

Pakavimo klasė pakartotiniam naudojimui

Nieko nėra taip gerai, kaip atrodo

Deja, klasių negalima eksportuoti pagal modulius taip, kaip funkcijos ar kintamieji; bet yra keletas gudrybių. Tarkime, kad jūsų klasės yra apibrėžtos faile ./my-classes.ps1

  • Galite naudoti taškais failą su klasėmis:. ./my-classes.ps1. Taip bus paleista my-classes.ps1 jūsų dabartinėje srityje ir apibrėžiamos visos ten esančio failo klasės.

  • Galite sukurti „Powershell“ modulį, kuris eksportuos visas tinkintas API (cmdlet), ir modulio apraše nustatyti kintamąjį ScriptsToProcess = "./my-classes.ps1" su tuo pačiu rezultatu: ./my-classes.ps1 bus vykdomas tavo aplinka.

Kad ir kurią parinktį pasirinktumėte, atminkite, kad „Powershell“ tipo sistema negali išspręsti to paties pavadinimo tipų, įkeltų iš skirtingų vietų.
Net jei įkėlėte dvi identiškas klases su tomis pačiomis savybėmis iš skirtingų vietų, rizikuojate susidurti su problemomis.

Kelias pirmyn

Geriausias būdas išvengti tipo skyros problemų – niekada neviešinti savo klasių vartotojams. Užuot tikėtis, kad vartotojas importuos klasės apibrėžtą tipą, eksportuokite iš savo modulio funkciją, kuri pašalina poreikį tiesiogiai pasiekti klasę. „Cluster“ atveju galime eksportuoti funkciją „New-Cluster“, kuri palaikys patogius parametrų rinkinius ir grąžins klasterį.

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

Ką dar skaityti

Apie klases
Gynybinis PowerShell
Funkcinis programavimas PowerShell

Šaltinis: www.habr.com

Добавить комментарий