پاورشل عملکردی با کلاس‌ها یک اکسیمورون نیست، من آن را تضمین می‌کنم

هی هابر! ترجمه مقاله را مورد توجه شما قرار می دهم "پاورشل کاربردی با کلاس ها.
قول می‌دهم که این یک اکسیمورون نیست"
توسط کریستوفر کوچ

پارادایم های برنامه نویسی شی گرا و تابعی ممکن است در تضاد با یکدیگر به نظر برسند، اما هر دو به یک اندازه در Powershell پشتیبانی می شوند. تقریباً همه زبان‌های برنامه‌نویسی، چه کاربردی و چه غیر کاربردی، دارای امکاناتی برای اتصال نام-مقدار گسترده هستند. کلاس ها، مانند ساختارها و رکوردها، تنها یک رویکرد هستند. اگر استفاده از کلاس‌ها را به پیوند نام‌ها و مقادیر محدود کنیم و از مفاهیم برنامه‌نویسی شی گرا مانند وراثت، چندشکلی یا تغییرپذیری اجتناب کنیم، می‌توانیم از مزایای آن‌ها بدون پیچیدگی کدمان استفاده کنیم. علاوه بر این، با اضافه کردن روش‌های تبدیل نوع تغییرناپذیر، می‌توانیم کد عملکردی خود را با کلاس‌ها غنی کنیم.

جادوی کاست ها

کاست ها یکی از قدرتمندترین ویژگی های پاورشل هستند. هنگامی که یک مقدار می‌دهید، به قابلیت‌های اولیه و اعتبارسنجی ضمنی که محیط به برنامه شما اضافه می‌کند تکیه می‌کنید. به عنوان مثال، به سادگی ریختن یک رشته در [xml] آن را از طریق کد تجزیه کننده اجرا می کند و یک درخت کامل xml ایجاد می کند. برای همین منظور می توانیم از Classes در کد خود استفاده کنیم.

بازیگران هشتبل

اگر سازنده ندارید، می‌توانید با فرستادن هشتتبل به نوع کلاس خود، بدون آن ادامه دهید. فراموش نکنید که از ویژگی های اعتبارسنجی برای استفاده کامل از این الگو استفاده کنید. در همان زمان، می‌توانیم از ویژگی‌های تایپ کلاس برای اجرای منطق اولیه اولیه و اعتبارسنجی عمیق‌تر استفاده کنیم.

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
}

علاوه بر این، ریخته گری به دریافت خروجی تمیز کمک می کند. خروجی آرایه hashtable Cluster ارسال شده به Format-Table را با آنچه که در ابتدا این هشتبل ها را در یک کلاس قرار داده اید، مقایسه کنید. ویژگی های یک کلاس همیشه به ترتیبی که در آنجا تعریف شده اند فهرست می شوند. فراموش نکنید که کلمه کلیدی پنهان را قبل از همه آن ویژگی هایی که نمی خواهید در نتایج قابل مشاهده باشند اضافه کنید.

پاورشل عملکردی با کلاس‌ها یک اکسیمورون نیست، من آن را تضمین می‌کنم

بازیگران معانی

اگر سازنده ای با یک آرگومان دارید، با فرستادن یک مقدار به نوع کلاس شما، مقدار را به سازنده شما منتقل می کند، جایی که می توانید نمونه ای از کلاس خود را مقداردهی اولیه کنید.

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]$cluster = Get-Content "./my-cluster.json" | ConvertFrom-Json
[Cluster[]]$clusters = Import-Csv "./my-clusters.csv"

در کد عملکردی شما قرار می گیرد

برنامه های کاربردی ابتدا ساختارهای داده را تعریف می کنند، سپس برنامه را به عنوان دنباله ای از تبدیل ها بر روی ساختارهای داده تغییرناپذیر پیاده سازی می کنند. علیرغم تصور متناقض، کلاس ها به لطف روش های تبدیل نوع، واقعاً به شما کمک می کنند تا کد عملکردی بنویسید.

آیا Powershell که می نویسم کاربردی است؟

بسیاری از افرادی که از C# یا پس‌زمینه‌های مشابه آمده‌اند در حال نوشتن Powershell هستند که شبیه به C# است. با انجام این کار، از استفاده از مفاهیم برنامه نویسی تابعی فاصله می گیرید و احتمالاً از غواصی شدید در برنامه نویسی شی گرا در Powershell یا یادگیری بیشتر در مورد برنامه نویسی تابعی سود خواهید برد.

اگر به شدت به تبدیل داده های تغییرناپذیر با استفاده از خطوط لوله (|)، Where-Object، ForEach-Object، Select-Object، Group-Object، Sort-Object و غیره تکیه می کنید - سبک کاربردی تری دارید و از استفاده از Powershell سود خواهید برد. کلاس ها به سبک عملکردی

استفاده کاربردی از کلاس ها

کاست ها، اگرچه از یک نحو جایگزین استفاده می کنند، اما فقط یک نقشه برداری بین دو حوزه هستند. در خط لوله، می توانید آرایه ای از مقادیر را با استفاده از ForEach-Object ترسیم کنید.

در مثال زیر، سازنده Node هر بار که یک Datum ریخته می شود، اجرا می شود، و این به ما این فرصت را می دهد که مقدار مناسبی از کد را ننویسیم. در نتیجه، خط لوله ما بر جست‌وجو و تجمیع داده‌های اعلامی تمرکز می‌کند، در حالی که کلاس‌های ما از تجزیه و اعتبارسنجی داده‌ها مراقبت می‌کنند.

# Пример комбинирования классов с конвейерами для 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 را در محدوده فعلی شما اجرا می کند و تمام کلاس ها را از فایل در آنجا تعریف می کند.

  • می‌توانید یک ماژول Powershell ایجاد کنید که تمام APIهای سفارشی (cmdlet) شما را صادر کند و متغیر ScriptsToProcess = "./my-classes.ps1" را در مانیفست ماژول خود تنظیم کنید، با نتیجه یکسان: ./my-classes.ps1 در محیط شما .

هر گزینه ای را که انتخاب می کنید، به خاطر داشته باشید که سیستم نوع Powershell نمی تواند انواعی با همان نام بارگذاری شده از مکان های مختلف را حل کند.
حتی اگر دو کلاس یکسان با ویژگی‌های یکسان را از مکان‌های مختلف بارگذاری کنید، در معرض خطر قرار می‌گیرید.

راه پیش رو

بهترین راه برای جلوگیری از مشکلات وضوح نوع این است که هرگز کلاس های خود را در معرض دید کاربران قرار ندهید. به جای اینکه انتظار داشته باشید کاربر یک نوع کلاس تعریف شده را وارد کند، تابعی را از ماژول خود صادر کنید که نیاز به دسترسی مستقیم به کلاس را برطرف می کند. برای Cluster، ما می توانیم یک تابع New-Cluster را صادر کنیم که از مجموعه پارامترهای کاربر پسند پشتیبانی می کند و یک 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

چه چیز دیگری برای خواندن

درباره کلاس ها
پاورشل دفاعی
برنامه نویسی کاربردی در پاورشل

منبع: www.habr.com

اضافه کردن نظر