Ангиудтай функциональ Powershell бол оксиморон биш, би үүнийг баталж байна

Хөөе Хабр! Би нийтлэлийн орчуулгыг та бүхэнд толилуулж байна "Ангиудтай функциональ PowerShell.
Энэ бол оксиморон биш гэдгийг би амлаж байна"
Кристофер Куеч.

Объект хандалтат болон функциональ програмчлалын парадигмууд нь хоорондоо зөрчилдсөн мэт санагдаж болох ч Powershell дээр хоёулаа адилхан дэмжигддэг. Бараг бүх програмчлалын хэл нь функциональ ч бай, үгүй ​​ч бай нэр, утгыг өргөтгөх боломжтой; Бүтэц, бичлэг гэх мэт ангиуд нь зөвхөн нэг арга юм. Хэрэв бид Classes-ийн хэрэглээгээ нэр, утгыг холбох замаар хязгаарлаж, удамшлын, полиморфизм эсвэл хувирах чадвар гэх мэт хүнд объект хандалтат програмчлалын ойлголтоос зайлсхийвэл бид кодоо хүндрүүлэхгүйгээр тэдгээрийн давуу талыг ашиглах боломжтой. Цаашилбал, хувиршгүй төрөл хувиргах аргуудыг нэмснээр бид функциональ кодоо Classes-ээр баяжуулж чадна.

Кастуудын ид шид

Кастууд нь Powershell-ийн хамгийн хүчирхэг шинж чанаруудын нэг юм. Та утгыг гаргахдаа орчин нь таны аппликешнд нэмдэг далд эхлүүлэх болон баталгаажуулах чадварт найдаж байна. Жишээлбэл, [xml]-д стринг хийснээр үүнийг задлан шинжлэлийн кодоор ажиллуулж, бүрэн xml мод үүсгэх болно. Бид ижил зорилгоор өөрийн кодонд Classes ашиглаж болно.

Хэшт хүснэгтүүдийг дамжуулах

Хэрэв танд бүтээгч байхгүй бол ангийн төрөлдөө hashtable-г оруулан бүтээгчгүйгээр үргэлжлүүлж болно. Энэ загварыг бүрэн ашиглахын тулд баталгаажуулалтын шинж чанаруудыг ашиглахаа бүү мартаарай. Үүний зэрэгцээ бид илүү гүнзгий эхлүүлэх, баталгаажуулах логикийг ажиллуулахын тулд ангийн бичсэн шинж чанаруудыг ашиглаж болно.

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
}

Үүнээс гадна цутгах нь цэвэр гарц гаргахад тусалдаг. Формат-Хүснэгт рүү дамжуулсан Cluster hashtable массивын гаралтыг та эхлээд эдгээр hashtables-ийг ангид оруулбал авах зүйлтэй харьцуул. Ангийн шинж чанаруудыг тэнд тодорхойлсон дарааллаар нь үргэлж жагсаадаг. Үр дүнд харагдахыг хүсэхгүй байгаа бүх шинж чанаруудын өмнө нуугдсан түлхүүр үгээ нэмэхээ бүү мартаарай.

Ангиудтай функциональ Powershell бол оксиморон биш, би үүнийг баталж байна

Утгын цуваа

Хэрэв танд нэг аргументтай бүтээгч байгаа бол өөрийн ангийн төрөлд утга оруулах нь тухайн утгыг таны байгуулагч руу дамжуулж, ангийнхаа жишээг эхлүүлэх боломжтой.

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"

Мөр рүү дамжуулах

Мөн та [string] ToString() ангийн аргыг хүчингүй болгож, мөрийн интерполяцыг ашиглах гэх мэт объектын стринг дүрслэлийн цаадах логикийг тодорхойлж болно.

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

Цуваачилсан тохиолдлуудыг дамжуулах

Дамжуулах нь аюулгүй цуваа арилгах боломжийг олгодог. Хэрэв өгөгдөл нь Кластер дээрх бидний тодорхойлолтод тохирохгүй бол доорх жишээнүүд амжилтгүй болно

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

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

Таны функциональ кодонд оруулдаг

Функциональ программууд нь эхлээд өгөгдлийн бүтцийг тодорхойлж, дараа нь программыг өөрчлөгддөггүй өгөгдлийн бүтцэд хувиргах дараалал болгон хэрэгжүүлдэг. Зөрчилтэй сэтгэгдэлтэй байсан ч ангиуд нь төрөл хөрвүүлэх аргуудын ачаар функциональ код бичихэд үнэхээр тусалдаг.

Миний бичиж байгаа Powershell ажиллагаатай юу?

C# эсвэл ижил төстэй суурьтай олон хүмүүс C#-тэй төстэй Powershell бичиж байна. Үүнийг хийснээр та функциональ програмчлалын үзэл баримтлалыг ашиглахаас татгалзаж байгаа бөгөөд Powershell дээр объект хандалтат програмчлалд гүнзгий шумбах эсвэл функциональ програмчлалын талаар илүү ихийг мэдэх нь ашигтай байх болно.

Хэрэв та дамжуулах хоолой (|), Where-Object, ForEach-Object, Select-Object, Group-Object, Sort-Object гэх мэтийг ашиглан хувиршгүй өгөгдлийг хувиргахад ихээхэн найдаж байгаа бол танд илүү функциональ загвар байх бөгөөд Powershell ашиглах нь танд ашигтай байх болно. функциональ хэв маягийн ангиуд.

Ангиудын функциональ хэрэглээ

Кастууд хэдийгээр альтернатив синтакс ашигладаг ч хоёр домайн хоорондын зураглал юм. Дамжуулах хоолойд та ForEach-Object ашиглан утгын массивыг зураглаж болно.

Доорх жишээнд Зангилаа үүсгэгч нь Датумыг дамжуулах болгонд гүйцэтгэгддэг бөгөөд энэ нь бидэнд хангалттай хэмжээний код бичихээс зайлсхийх боломжийг олгодог. Үүний үр дүнд манай дамжлага нь өгөгдлийн мэдүүлэг асуулга, нэгтгэхэд анхаарлаа төвлөрүүлдэг бол манай ангиуд өгөгдөл задлан шинжлэх, баталгаажуулах ажлыг гүйцэтгэдэг.

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

Дахин ашиглах савлагааны анги

Санаж байгаа шиг сайхан зүйл байхгүй

Харамсалтай нь, ангиудыг функц эсвэл хувьсагчийн нэгэн адил модулиар экспортлох боломжгүй; гэхдээ зарим нэг заль мэх байдаг. Таны ангиудыг ./my-classes.ps1 файлд тодорхойлсон гэж бодъё

  • Та анги бүхий файлыг дотоор авч болно:. ./my-classes.ps1. Энэ нь таны одоогийн хамрах хүрээнд my-classes.ps1-г ажиллуулж, тэнд байгаа файлын бүх ангиудыг тодорхойлно.

  • Та өөрийн бүх тусгай API (cmdlet)-уудыг экспортлох Powershell модулийг үүсгэж, модулийн манифест дахь ScriptsToProcess = "./my-classes.ps1" хувьсагчийг тохируулах боломжтой бөгөөд ижил үр дүн: ./my-classes.ps1 нь дараах байдлаар ажиллах болно. таны орчин.

Аль ч сонголтыг сонгосон Powershell-ийн төрлийн систем нь өөр өөр газраас ачаалагдсан ижил нэртэй төрлүүдийг шийдэж чадахгүй гэдгийг санаарай.
Та өөр газраас ижил шинж чанартай хоёр ижил ангиудыг ачаалсан ч асуудалд орох эрсдэлтэй.

Урагшлах зам

Төрөл нарийвчлалын асуудлаас зайлсхийх хамгийн сайн арга бол ангиудаа хэзээ ч хэрэглэгчдэд үзүүлэхгүй байх явдал юм. Хэрэглэгчээс ангиллаар тодорхойлсон төрлийг импортлохыг хүлээхийн оронд модулиасаа тухайн ангид шууд хандах шаардлагагүй функцийг экспортлоорой. Кластерын хувьд бид хэрэглэгчдэд ээлтэй параметрийн багцыг дэмжиж, 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

Өөр юу унших вэ

Ангиудын тухай
Хамгаалалтын PowerShell
PowerShell дахь функциональ програмчлал

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх