Ang Functional Powershell na may mga klase ay hindi isang oxymoron, ginagarantiya ko ito

Hello, Habr! Ipinakita ko sa iyong pansin ang isang pagsasalin ng artikulo "Functional na PowerShell na may mga Klase.
Ipinapangako ko na hindi ito isang oxymoron"
ni Christopher Kuech.

Ang Object-oriented at functional na mga paradigm sa programming ay maaaring mukhang magkasalungat sa isa't isa, ngunit pareho silang sinusuportahan sa Powershell. Halos lahat ng programming language, functional man o hindi, ay may mga pasilidad para sa pinalawig na name-value binding; Ang mga klase, tulad ng mga istruktura at talaan, ay isang diskarte lamang. Kung nililimitahan namin ang aming paggamit ng Mga Klase sa pagbubuklod ng mga pangalan at halaga, at iiwasan namin ang mabibigat na object-oriented na mga konsepto ng programming gaya ng inheritance, polymorphism, o mutability, maaari naming samantalahin ang mga benepisyo ng mga ito nang hindi kumplikado ang aming code. Dagdag pa, sa pamamagitan ng pagdaragdag ng hindi nababagong uri ng mga paraan ng conversion, maaari naming pagyamanin ang aming functional code gamit ang Mga Klase.

Ang mahika ng mga caste

Ang mga cast ay isa sa pinakamakapangyarihang feature sa Powershell. Kapag nag-cast ka ng halaga, umaasa ka sa mga implicit na pagpapasimula at mga kakayahan sa pagpapatunay na idinaragdag ng environment sa iyong application. Halimbawa, ang pag-cast lang ng string sa [xml] ay magpapatakbo nito sa parser code at bubuo ng kumpletong xml tree. Maaari naming gamitin ang Mga Klase sa aming code para sa parehong layunin.

Mag-cast ng mga hashtable

Kung wala kang constructor, maaari kang magpatuloy nang walang isa sa pamamagitan ng pag-cast ng hashtable sa uri ng iyong klase. Huwag kalimutang gamitin ang mga katangian ng pagpapatunay upang lubos na mapakinabangan ang pattern na ito. Kasabay nito, maaari nating gamitin ang mga nai-type na katangian ng klase upang magpatakbo ng mas malalim na pagsisimula at lohika ng pagpapatunay.

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
}

Bilang karagdagan, ang paghahagis ay nakakatulong upang makakuha ng malinis na output. Ihambing ang output ng Cluster hashtable array na ipinasa sa Format-Table sa kung ano ang makukuha mo kung una mong i-cast ang mga hashtable na ito sa isang klase. Ang mga katangian ng isang klase ay palaging nakalista sa pagkakasunud-sunod kung saan ang mga ito ay tinukoy doon. Huwag kalimutang idagdag ang nakatagong keyword bago ang lahat ng mga pag-aari na hindi mo gustong makita sa mga resulta.

Ang Functional Powershell na may mga klase ay hindi isang oxymoron, ginagarantiya ko ito

Cast ng mga kahulugan

Kung mayroon kang constructor na may isang argumento, ang paglalagay ng value sa uri ng iyong klase ay ipapasa ang value sa iyong constructor, kung saan maaari mong simulan ang isang instance ng iyong klase

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"

I-cast sa linya

Maaari mo ring i-override ang [string] ToString() class method para tukuyin ang logic sa likod ng representasyon ng string ng object, gaya ng paggamit ng string interpolation.

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

Mag-cast ng mga serialized na instance

Pinapayagan ng cast ang ligtas na deserialization. Mabibigo ang mga halimbawa sa ibaba kung hindi matugunan ng data ang aming detalye sa Cluster

# Валидация сСриализованных Π΄Π°Π½Π½Ρ‹Ρ…

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

Mga cast sa iyong functional code

Ang mga functional na programa ay unang tumutukoy sa mga istruktura ng data, pagkatapos ay ipinatupad ang programa bilang isang pagkakasunud-sunod ng mga pagbabago sa mga hindi nababagong istruktura ng data. Sa kabila ng magkasalungat na impression, ang mga klase ay talagang nakakatulong sa iyo na magsulat ng functional code salamat sa uri ng mga paraan ng conversion.

Gumagana ba ang Powershell na sinusulat ko?

Maraming tao na nagmumula sa C# o katulad na background ang sumusulat ng Powershell, na katulad ng C#. Sa paggawa nito, lumalayo ka na sa paggamit ng mga functional programming concepts at malamang na makikinabang sa pag-dive nang husto sa object-oriented programming sa Powershell o pag-aaral pa tungkol sa functional programming.

Kung lubos kang umaasa sa pagbabago ng hindi nababagong data gamit ang mga pipeline (|), Where-Object, ForEach-Object, Select-Object, Group-Object, Sort-Object, atbp. - mayroon kang mas functional na istilo at makikinabang ka sa paggamit ng Powershell mga klase sa isang functional na istilo.

Functional na paggamit ng mga klase

Ang mga caste, bagama't gumagamit sila ng alternatibong syntax, ay isang pagmamapa lamang sa pagitan ng dalawang domain. Sa pipeline, maaari mong imapa ang isang hanay ng mga halaga gamit ang ForEach-Object.

Sa halimbawa sa ibaba, ang Node constructor ay isinasagawa sa bawat oras na ang isang Datum ay na-cast, at ito ay nagbibigay sa amin ng pagkakataon na hindi magsulat ng isang patas na dami ng code. Bilang resulta, ang aming pipeline ay nakatuon sa deklaratibong pag-query at pagsasama-sama ng data, habang ang aming mga klase ang nangangasiwa sa pag-parse at pagpapatunay ng data.

# ΠŸΡ€ΠΈΠΌΠ΅Ρ€ комбинирования классов с ΠΊΠΎΠ½Π²Π΅ΠΉΠ΅Ρ€Π°ΠΌΠΈ для 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

Packaging class para magamit muli

Walang kasing ganda sa tila

Sa kasamaang palad, ang mga klase ay hindi maaaring i-export ng mga module sa parehong paraan tulad ng mga function o variable; ngunit may ilang mga trick. Sabihin nating ang iyong mga klase ay tinukoy sa file na ./my-classes.ps1

  • Maaari kang mag-dotsource ng isang file na may mga klase:. ./my-classes.ps1. Ipapatupad nito ang my-classes.ps1 sa iyong kasalukuyang saklaw at tutukuyin ang lahat ng mga klase mula sa file doon.

  • Maaari kang lumikha ng Powershell module na nag-e-export ng lahat ng iyong custom na API (cmdlets) at itakda ang ScriptsToProcess = "./my-classes.ps1" na variable sa iyong module manifest, na may parehong resulta: ./my-classes.ps1 ay isasagawa sa iyong kapaligiran.

Alinmang opsyon ang pipiliin mo, tandaan na hindi malulutas ng system ng uri ng Powershell ang mga uri ng parehong pangalan na na-load mula sa iba't ibang lugar.
Kahit na nag-load ka ng dalawang magkaparehong klase na may parehong mga katangian mula sa magkaibang mga lugar, nanganganib kang magkaroon ng mga problema.

Ang daan pasulong

Ang pinakamahusay na paraan upang maiwasan ang mga problema sa paglutas ng uri ay ang hindi kailanman ilantad ang iyong mga klase sa mga user. Sa halip na asahan ang user na mag-import ng uri na tinukoy ng klase, mag-export ng function mula sa iyong module na nag-aalis ng pangangailangang direktang ma-access ang klase. Para sa Cluster, maaari kaming mag-export ng New-Cluster function na susuporta sa user-friendly na mga set ng parameter at magbabalik ng Cluster.

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

Kung anu-ano pang babasahin

Tungkol sa Mga Klase
Depensibong PowerShell
Functional Programming sa PowerShell

Pinagmulan: www.habr.com

Magdagdag ng komento