Funktsionaalne Powershell koos klassidega ei ole oksüümoron, garanteerin selle

Tere Habr! Esitan teie tähelepanu artikli tõlkele "Funktsionaalne PowerShell koos klassidega.
Ma luban, et see pole oksüümoron"
autor Christopher Kuech.

Objektorienteeritud ja funktsionaalse programmeerimise paradigmad võivad tunduda üksteisega vastuolus, kuid Powershellis toetatakse mõlemat võrdselt. Peaaegu kõik programmeerimiskeeled, olgu need funktsionaalsed või mitte, sisaldavad laiendatud nime-väärtuse sidumise võimalusi; Klassid, nagu struktuurid ja kirjed, on vaid üks lähenemisviis. Kui piirame klasside kasutamist nimede ja väärtuste sidumisega ning väldime raskeid objektorienteeritud programmeerimiskontseptsioone, nagu pärimine, polümorfism või muutuvus, saame neid ära kasutada ilma oma koodi keerulisemaks muutmata. Lisaks saame muutumatute tüüpide teisendusmeetodite lisamisega oma funktsionaalset koodi klassidega rikastada.

Kastide maagia

Kastid on Powershelli üks võimsamaid funktsioone. Väärtuse ülekandmisel tuginete kaudsetele lähtestamis- ja valideerimisvõimalustele, mille keskkond teie rakendusele lisab. Näiteks lihtsalt stringi ülekandmine [xml]-s käivitab selle parserikoodi ja loob täieliku xml-puu. Samal eesmärgil saame oma koodis kasutada klasse.

Cast hashtables

Kui teil pole konstruktorit, saate jätkata ilma selleta, kandes oma klassitüübile räsitabeli. Ärge unustage kasutada valideerimisatribuute, et seda mustrit täielikult ära kasutada. Samal ajal saame kasutada klassi tipitud atribuute veelgi sügavama initsialiseerimis- ja valideerimisloogika käivitamiseks.

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 casting saada puhast väljundit. Võrrelge Format-Table'ile edastatud klastri räsitabeli massiivi väljundit sellega, mida saate nende räsitabelite esmakordsel klassi kandmisel. Klassi omadused on alati loetletud selles järjekorras, milles need seal määratletakse. Ärge unustage lisada peidetud märksõna enne kõiki neid omadusi, mida te ei soovi tulemustes näha.

Funktsionaalne Powershell koos klassidega ei ole oksüümoron, garanteerin selle

Tähenduste loomine

Kui teil on ühe argumendiga konstruktor, edastab klassitüübile väärtuse ülekandmine selle 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"

Valage joonele

Samuti saate alistada klassi meetodi [string] ToString(), et määratleda objekti stringi esituse taga olevat loogikat, 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 eksemplaride ülekandmine

Cast võimaldab ohutut deserialiseerimist. Allpool toodud näited ebaõnnestuvad, kui andmed ei vasta meie klastri spetsifikatsioonidele

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

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

Kastib teie funktsionaalsesse koodi

Funktsionaalsed programmid määratlevad esmalt andmestruktuurid, seejärel rakendavad programmi muutumatute andmestruktuuride teisenduste jadana. Vaatamata vastuolulisele muljele aitavad klassid tõesti tänu tüübiteisendusmeetoditele funktsionaalset koodi kirjutada.

Kas Powershell, mida ma kirjutan, töötab?

Paljud C# või sarnase taustaga inimesed kirjutavad Powershelli, mis sarnaneb C#-ga. Seda tehes loobute funktsionaalsete programmeerimiskontseptsioonide kasutamisest ja tõenäoliselt oleks kasulik sukelduda Powershelli objektorienteeritud programmeerimisse või õppida funktsionaalse programmeerimise kohta lisateavet.

Kui tuginete suurel määral muutumatute andmete teisendamisele torujuhtmete (|), Kus-Objekt, ForEach-Object, Select-Object, Group-Object, Sort-Object jne abil, on teil funktsionaalsem stiil ja teile on kasulik Powershelli kasutamine. klassid funktsionaalses stiilis.

Klasside funktsionaalne kasutamine

Kastid, kuigi nad kasutavad alternatiivset süntaksit, on vaid kahe domeeni vaheline vastendus. Konveieril saate ForEach-Object abil kaardistada väärtuste massiivi.

Allolevas näites käivitatakse sõlmekonstruktor iga kord, kui nullpunkti heidetakse, ja see annab meile võimaluse mitte kirjutada päris palju koodi. Selle tulemusena keskendub meie konveieri deklaratiivsetele andmete päringutele ja koondamisele, samas kui meie klassid hoolitsevad andmete sõelumise ja valideerimise eest.

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

Taaskasutamiseks mõeldud pakendiklass

Miski pole nii hea, kui tundub

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

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

  • Saate luua Powershelli mooduli, mis ekspordib kõik teie kohandatud API-d (cmdletid) ja määrata mooduli manifestis muutuja ScriptsToProcess = "./my-classes.ps1", sama tulemusega: ./my-classes.ps1 käivitatakse teie keskkond.

Ükskõik millise valiku valite, pidage meeles, et Powershelli tüübisüsteem ei suuda lahendada erinevatest kohtadest laaditud sama nimega tüüpe.
Isegi kui laadisite erinevatest kohtadest kaks identset klassi, millel on samad omadused, võib teil tekkida probleeme.

Edasine tee

Parim viis tüübilahutusprobleemide vältimiseks on mitte kunagi avaldada oma klasse kasutajatele. Selle asemel, et eeldada, et kasutaja impordiks klassi määratud tüübi, eksportige oma moodulist funktsioon, mis välistab vajaduse klassile otse juurde pääseda. Clusteri jaoks saame eksportida funktsiooni New-Cluster, mis toetab kasutajasõbralikke parameetrikomplekte ja tagastab klastri.

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 muud lugeda

Klasside kohta
Kaitsev PowerShell
Funktsionaalne programmeerimine PowerShellis

Allikas: www.habr.com

Lisa kommentaar