A funkcionális Powershell osztályokkal — ez nem oxímoron, garantálom.

Helló, Habr! Örömmel mutatom be az írás fordítását. "Funkcionális PowerShell osztályokkal.
Ígérem, hogy nem oxímoron"
szerző Christopher Kuech.

Az objektumorientált és funkcionális programozási paradigmák elsőre ellentmondásosnak tűnhetnek, de mindkettőt egyformán támogatja a PowerShell. Szinte minden programozási nyelv, függetlenül attól, hogy funkcionális-e vagy sem, rendelkezik fejlett névkötési és értékkötési eszközökkel; Az osztályok, akárcsak a struct-ok és record-ok, egy megközelítést képviselnek. Ha az osztályok használatát a névkötésre és értékkötésre korlátozzuk, és elkerüljük az olyan „nehézkes” objektumorientált programozási koncepciókat, mint az öröklés, polimorfizmus vagy mutabilitás, kihasználhatjuk az előnyeiket anélkül, hogy bonyolítanánk a kódunkat. Továbbá, ha immutálható típusátalakító metódusokat adunk hozzá, gazdagíthatjuk a funkcionális kódunkat osztályokkal.

A típusok varázsa

A típusok az egyik legnagyobb funkciója a PowerShell-nek. Amikor egy értéket típussá alakít, a környezet által az alkalmazásához hozzáadott implicit inicializálásra és validálásra támaszkodik. Például egy egyszerű string átalakítása [xml]-e átfuttatja azt a parser kódján, és létrehozza a teljes XML fát. A kódunkban az osztályokat ugyanazzal a céllal használhatjuk.

Típus a hash-táblákhoz

Ha nincs konstruktorod, folytathatod nélküle is, úgy, hogy hash-táblákat keresel a klassz típusához. Ne felejtsd el kihasználni a validációs attribútumokat, hogy teljes mértékben kiaknázhasd ezt a mintát. Ezen kívül használhatjuk a típusos osztálytulajdonságokat, hogy még mélyebb inicializálási és érvényesítési logikát indítsunk el.

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

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

Ezenkívül a típusok segítenek a tiszta kimenet megszerzésében. Hasonlítsd össze a Cluster hash-táblák tömbjének Formátum-Table-be továbbított kimenetét azzal, amit kapsz, ha ezeket a hash-táblákat osztályokká alakítod. Az osztály tulajdonságai mindig abban a sorrendben kerülnek felsorolásra, ahogy definiálták őket. Ne felejtsd el hozzáadni a hidden kulcsszót minden olyan tulajdonsághoz, amelyeknek nem szabad látszódniuk a kimenetben.

A funkcionális Powershell osztályokkal — ez nem oxímoron, garantálom.

Értékek típusa

Ha egy egyértelmű adattípussal rendelkező konstruktorod van, akkor az értéket az osztályod típusára konvertálva átadja az értéket a konstruktorodnak, ahol inicializálhatod az osztályod példányát.

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"

Stringré való konverzió

A [string] ToString() osztály módszer felülírható az objektum szöveges reprezentációjának logikájának meghatározására, például a használatával szöveginterpolációval.

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 "Most hoztunk létre egy modellt a '$cluster' számára"

A sorozatpéldányok konvertálása

A konverzió lehetővé teszi a biztonságos deszerializálást. Az alábbi példák hibával végződnek, ha az adatok nem felelnek meg a Cluster specifikációjának.

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

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

A konverziók a funkcionális kódodban

A funkcionális programok először az adatstruktúrákat definiálják, majd megvalósítják a programot mint átalakítások sorozatát a változatlan adatstruktúrákon. Annak ellenére, hogy ez ellentmondásos benyomást kelt, az osztályok valójában segítenek funkcionális kód írásában a típusok konvertálásának módszerei segítségével.

Funkcionális Powershell-t írok?

Sokan, akik a C#-ból vagy hasonló háttérrel érkeztek, olyan Powershell-t írnak, amely hasonlít a C#-ra. Ezzel a megközelítéssel elhagyod a funkcionális programozás koncepcióit, és valószínűleg előnyödre válik, ha mélyebben belemerülsz a Powershell objektumorientált programozásába, vagy jobban megismered a funkcionális programozást.

Ha mélyen támaszkodsz a változatlan adatok átalakítására, a csövek (|), Where-Object, ForEach-Object, Select-Object, Group-Object, Sort-Object stb. használatával — akkor van egy funkcionális stílusod, és jól fog segíteni a Powershell osztályok funkcionális stílusban való használata.

Funkcionális osztályhasználat

A típusok, bár alternatív szintaxist használnak, csupán egy leképezés a két domén között. A csővezetékben egy értékekből álló tömböt térképezhetünk fel a ForEach-Object használatával.

A következő példában a Node konstruktor minden alkalommal végrehajtásra kerül, amikor a Datum-ra típuscsere történik, és ez lehetővé teszi, hogy ne írjunk meg számos kódot. Ennek eredményeképpen a csővezetékünk a deklaratív adatlekérdezésre és aggregációra összpontosít, míg az osztályaink magukra vállalják az adatok elemzését és érvényesítését.

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

Osztálycsomag a további felhasználásért

Nincs semmi, ami annyira jó lenne, mint amilyennek tűnik.

Sajnos az osztályok nem exportálhatók a modulokból ugyanúgy, mint a funkciók vagy változók; de van néhány trükk. Tegyük fel, hogy az osztályaik a .\/my-classes.ps1 fájlban lettek meghatározva.

  • Fájlok osztályokkal való dot sourcingot végezhet:. .\/my-classes.ps1. Ez a my-classes.ps1 fájlt végrehajtja az aktuális láthatóságában, és az összes osztályt meghatározza a fájlból.

  • Készíthet egy Powershell modult, amely exportálja az összes egyedi API-ját (parancsmintáit), és beállíthatja a ScriptsToProcess változót = ".\/my-classes.ps1" a moduljának manifestjében, ugyanezzel az eredménnyel: .\/my-classes.ps1 végrehajtódik a környezetében.

Bármelyik lehetőséget is válassza, ne feledje, hogy a Powershell típusainak rendszere nem tudja kezelni az azonos nevű típusokat, amelyeket különböző helyekről töltenek be.
Még ha két azonos osztályt töltött is be különböző helyekről, amelyek azonos tulajdonságokkal rendelkeznek, problémákba futhat.

Az előre vezető út

A legjobb módja annak, hogy elkerülje a típusokkal kapcsolatos problémákat, ha soha nem teszi elérhetővé az osztályait a felhasználók számára. Ahelyett, hogy azt várná el, hogy a felhasználó egy bizonyos osztálytípust importáljon, exportáljon a moduljából egy olyan funkciót, amely lehetővé teszi, hogy közvetlenül ne kelljen foglalkoznia az osztállyal. A Cluster vonatkozásában exportálhatunk egy New-Cluster funkciót, amely támogatja a felhasználóbarát paramétersorozatokat, és visszaadja a Clustert.

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

Mit olvass még

Osztályok Ról
Védelmi PowerShell
Funkcionális Programozás PowerShell-ben

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster