نگاهی به نحوهی عملکرد سرویسها و تزریق وابستگیها در AngularJS 2.0
فرض کنید کلاس سرویسی، به نحو ذیل تعریف شدهاست:
export class MyService {}
let svc = new MyService();
همچنین در این حالت، mocking این سرویس برای نوشتن unit tests نیز مشکل میباشد.
راه بهتر و توصیه شدهی در اینجا، ثبت و معرفی این سرویسها به AngularJS 2.0 است. سپس AngularJS 2.0 به ازای هر کلاس سرویس معرفی شدهی به آن، یک وهله/نمونه را ایجاد میکند. بنابراین طول عمر سرویسهای ایجاد شدهی در این حالت، singleton است (یکبار ایجاد شده و تا پایان طول عمر برنامه زنده نگه داشته میشوند).
پس از آن میتوان از تزریق کنندههای توکار AngularJS 2.0، جهت تزریق وهلههای این سرویسها استفاده کرد.
اکنون اگر کلاسی، نیاز به این سرویس داشته باشد، نیاز خود را به صورت یک وابستگی تعریف شدهی در سازندهی کلاس اعلام میکند:
constructor(private _myService: MyService){}
به این فرآیند اصطلاحا dependency injection و یا تزریق وابستگیها میگویند. در فرآیند تزریق وابستگیها، یک کلاس، وهلههای کلاسهای دیگر مورد نیاز خودش را بجای وهله سازی مستقیم، از یک تزریق کننده دریافت میکند. بنابراین بجای نوشتن newها در کلاس جاری، آنها را به صورت وابستگیهایی در سازندهی کلاس تعریف میکنیم تا توسط AngularJS 2.0 تامین شوند.
با توجه به اینکه طول عمر این وابستگیها singleton است و این طول عمر توسط AngularJS 2.0 مدیریت میشود، اطلاعات وهلههای سرویسهای مختلف و تغییرات صورت گرفتهی در آنها، بین تمام کامپوننتها به صورت یکسانی به اشتراک گذاشته میشوند.
به علاوه اکنون امکان mocking سرویسها با توجه به عدم وهله سازی آنها در داخل کلاسها به صورت مستقیم، سادهتر از قبل میسر است.
مراحل ساخت یک سرویس در AngularJS 2.0
ساخت یک سرویس در AngularJS 2.0، با ایجاد یک کلاس جدید شروع میشود. سپس متادیتای آن افزوده شده و در آخر موارد مورد نیاز آن import خواهند شد. با این موارد پیشتر در حین ساختن یک کامپوننت جدید و یا یک Pipe جدید آشنا شدهاید و این طراحی یک دست را در سراسر AngularJS 2.0 میتوان مشاهده کرد.
اولین سرویس خود را با افزودن فایل جدید product.service.ts به پوشهی app\products آغاز میکنیم؛ با این محتوا:
import { Injectable } from 'angular2/core'; import { IProduct } from './product'; @Injectable() export class ProductService { getProducts(): IProduct[] { return [ { "productId": 2, "productName": "Garden Cart", "productCode": "GDN-0023", "releaseDate": "March 18, 2016", "description": "15 gallon capacity rolling garden cart", "price": 32.99, "starRating": 4.2, "imageUrl": "app/assets/images/garden_cart.png" }, { "productId": 5, "productName": "Hammer", "productCode": "TBX-0048", "releaseDate": "May 21, 2016", "description": "Curved claw steel hammer", "price": 8.9, "starRating": 4.8, "imageUrl": "app/assets/images/rejon_Hammer.png" } ]; } }
همانند سایر ماژولهای تعریف شده، در اینجا نیز باید کلاس تعریف شده export شود تا در قسمتهای دیگر قابل استفاده و دسترسی گردد.
سپس در این سرویس، یک متد برای بازگشت لیست محصولات ایجاد شدهاست.
در ادامه یک decorator جدید به نام ()Injectable@ به بالای این کلاس اضافه شدهاست. این متادیتا است که مشخص میکند کلاس جاری، یک سرویس AngularJS 2.0 است.
البته باید دقت داشت که این مزین کننده تنها زمانی نیاز است حتما قید شود که کلاس تعریف شده، دارای وابستگیهای تزریق شدهای باشد. اما توصیه شدهاست که بهتر است هر کلاس سرویسی (حتی اگر دارای وابستگیهای تزریق شدهای هم نبود) به این decorator ویژه، مزین شود تا بتوان طراحی یک دستی را در سراسر برنامه شاهد بود.
در آخر هم موارد مورد نیاز، import میشوند. برای مثال Injectable در ماژول angular2/core تعریف شدهاست.
هدف از تعریف این سرویس، دور کردن وظیفهی تامین داده، از کلاس کامپوننت لیست محصولات است؛ جهت رسیدن به یک طراحی SOLID.
در قسمت بعدی این سری، این لیست را بجای یک آرایهی از پیش تعریف شده، از یک سرور HTTP دریافت خواهیم کرد.
ثبت و معرفی سرویس جدید ProductService به AngularJS 2.0 Injector
مرحلهی اول استفاده از سرویسهای تعریف شده، ثبت و معرفی آنها به AngularJS 2.0 Injector است. سپس این Injector است که تک وهلهی سرویس ثبت شدهی در آنرا در اختیار هر کامپوننتی که آنرا درخواست کند، قرار میدهد.
مرحلهی ثبت این سرویس، معرفی نام این کلاس، به خاصیتی آرایهای، به نام providers است که یکی از خواص decorator ویژهی Component است. بدیهی است هر کامپوننتی که در برنامه وجود داشته باشد، توانایی ثبت این سرویس را نیز دارد؛ اما باید از کدامیک استفاده کرد؟
اگر سرویس خود را در کامپوننت لیست محصولات رجیستر کنیم، تک وهلهی این سرویس تنها در این کامپوننت و زیر کامپوننتهای آن در دسترس خواهند بود و اگر این سرویس را در بیش از یک کامپوننت ثبت کنیم، آنگاه دیگر هدف اصلی طول عمر singleton یک سرویس مفهومی نداشته و برنامه هم اکنون دارای چندین وهله از سرویس تعریف شدهی ما میگردد و دیگر نمیتوان اطلاعات یکسانی را بین کامپوننتها به اشتراک گذاشت.
بنابراین توصیه شدهاست که از خاصیت providers کامپوننتهای غیر ریشهای، صرفنظر کرده و سرویسهای خود را تنها در بالاترین سطح کامپوننتهای تعریف شده، یعنی در فایل app.component.ts ثبت و معرفی کنید. به این ترتیب تک وهلهی ایجاد شدهی در اینجا، در این کامپوننت ریشهای و تمام زیر کامپوننتهای آن (یعنی تمام کامپوننتهای دیگر برنامه) به صورت یکسانی در دسترس قرار میگیرد.
به همین جهت فایل app.component.ts را گشوده و تغییرات ذیل را به آن اعمال کنید:
import { Component } from 'angular2/core'; import { ProductListComponent } from './products/product-list.component'; import { ProductService } from './products/product.service'; @Component({ selector: 'pm-app', template:` <div><h1>{{pageTitle}}</h1> <pm-products></pm-products> </div> `, directives: [ProductListComponent], providers: [ProductService] }) export class AppComponent { pageTitle: string = "DNT AngularJS 2.0 APP"; }
الف) خاصیت providers که آرایهای از سرویسها را قبول میکند، با ProductService مقدار دهی شدهاست.
ب) در ابتدای فایل، ProductService، از ماژول آن import گردیدهاست.
تزریق سرویسها به کامپوننتها
تا اینجا یک سرویس جدید را ایجاد کردیم و سپس آنرا به AngularJS 2.0 Injector معرفی نمودیم. اکنون نوبت به استفاده و تزریق آن، به کلاسی است که به این وابستگی نیاز دارد. در TypeScript، تزریق وابستگیها در سازندهی یک کلاس صورت میگیرند. هر کلاس، دارای متد سازندهای است که در زمان وهله سازی آن، اجرا میشود. اگر نیاز به تزریق وابستگیها باشد، تعریف این سازنده به صورت صریح، ضروری است. باید دقت داشت که هدف اصلی از متد سازنده، آغاز و مقدار دهی متغیرها و وابستگیهای مورد نیاز یک کلاس است و باید تا حد امکان از منطقهای طولانی عاری باشد.
در ادامه فایل product-list.component.ts را گشوده و سپس سازندهی ذیل را به آن اضافه کنید:
import { ProductService } from './product.service'; export class ProductListComponent implements OnInit { pageTitle: string = 'Product List'; imageWidth: number = 50; imageMargin: number = 2; showImage: boolean = false; listFilter: string = 'cart'; constructor(private _productService: ProductService) { }
روش خلاصه شدهای که در اینجا جهت تعریف سازندهی کلاس و متغیر تعریف شدهی در آن بکار گرفته شده، معادل قطعه کد متداول ذیل است و هر دو حالت ذکر شده، در TypeScript یکی میباشند:
private _productService: ProductService; constructor(productService: ProductService) { _productService = productService; }
این وابستگی در اولین باری که کلاس کامپوننت، توسط AngularJS 2.0 وهله سازی میشود، از لیست providers ثبت شدهی در کامپوننت ریشهی سایت، تامین خواهد شد.
اکنون نوبت به استفادهی از این سرویس تزریق شدهاست. به همین جهت ابتدا لیست عناصر آرایهی خاصیت products را حذف میکنیم (برای اینکه قرار است این سرویس، کار تامین اطلاعات را انجام دهد و نه کلاس کامپوننت).
products: IProduct[];
this.products = _productService.getProducts();
به همین جهت روش صحیح انجام این مقدار دهی، با پیاده سازی life cycle hook ویژهای به نام OnInit است که در قسمت پنجم آنرا معرفی کردیم:
export class ProductListComponent implements OnInit { products: IProduct[]; constructor(private _productService: ProductService) { } ngOnInit(): void { //console.log('In OnInit'); this.products = this._productService.getProducts(); }
در اینجا اکنون خاصیت products عاری است از ذکر صریح عناصر تشکیل دهندهی آن. سپس وابستگی مورد نیاز، در سازندهی کلاس تزریق شدهاست و در آخر، در رویداد چرخهی حیات ngOnInit، با استفاده از این وابستگی تزریقی، لیست محصولات دریافت و به خاصیت عمومی products نسبت داده شدهاست.
در ادامه برنامه را اجرا کنید. باید هنوز هم مطابق قبل، لیست محصولات قابل مشاهده باشد.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: MVC5Angular2.part7.zip
خلاصهی بحث
فرآیند کلی تعریف یک سرویس AngularJS 2.0، تفاوتی با ساخت یک کامپوننت یا Pipe سفارشی ندارد. پس از تعریف کلاسی که نام آن ختم شدهی به Service است، آنرا مزین به ()Injectable@ میکنیم. سپس این سرویس را در بالاترین سطح کامپوننتهای موجود یا همان کامپوننت ریشهی سایت، ثبت و معرفی میکنیم؛ تا تنها یک وهله از آن توسط AngularJS 2.0 Injector ایجاد شده و در اختیار تمام کامپوننتهای برنامه قرار گیرد. البته اگر این سرویس تنها در یک کامپوننت استفاده میشود و قصد به اشتراک گذاری اطلاعات آنرا نداریم، میتوان سطح سلسله مراتب دسترسی به آنرا نیز کاهش داد. برای مثال این سرویس را در لیست providers همان کامپوننت ویژه، ثبت و معرفی کرد. به این ترتیب تنها این کامپوننت خاص و فرزندان آن دسترسی به امکانات سرویس مدنظر را مییابند و نه تمام کامپوننتهای دیگر تعریف شدهی در برنامه.
در ادامه هر کلاسی که به این سرویس نیاز دارد (با توجه به سلسه مراتب دسترسی ذکر شده)، تنها کافی است در سازندهی خود، این وابستگی را اعلام کند تا توسط AngularJS 2.0 Injector تامین گردد.
نگاهی به Duende IdentityServer 5
Securing your application is bloody important. With so much jargon to sift through, it’s easy to get lost, for example there’s SSO, OAuth2, SAML 2.0, OpenID Connect, Federated Identity, 2FA, & MFA. Just to name a few! 😱 In this talk, Anthony will take an in depth look at Federated Identity using OpenID Connect and OAuth2 Framework for ASP. NET Core using Duende IdentityServer (aka IdentityServer 5). You will walk away knowing how to navigate the security options and avoid the madness.
چرا برنامه نویسی موبایل؟
با افزایش روزافزون SmartPhone ها و تبلتها، بازار تکنولوژی به این سمت سوق پیدا کردهاست. از این رو شرکتهای ارائه دهنده نرم افزاری، از این موقعیت استفاده کرده و هر کدام پلتفرم متفاوتی را برای برنامه نویسی بر روی این اسمارت فونها ارائه دادهاند. یکی از بزرگترین دغدغههای امروزه شرکتهای برنامه نویسی و توسعه نرم افزار موبایل، انتخاب درست پلتفرم برای توسعه نرم افزار میباشد. در این مقاله قصد دارم یکی از این پلت فرمها را بررسی کرده و معرفی کنم.
شرکت xamarin کار خود را در سال 2011 با ارایه نسخه Cross Platform پلتفرم .Net به نام Mono آغاز کرد. بعد از ارایه این نسخه از .Net، زامارین به کمک Mono توانست پیاده سازی بر روی Android و IOS را به نامهای MonoForAndroid و MonoTouch ارایه دهد. بعد از این نسخهها برنامه نویسان توانستند بر روی سیستم عاملهای اندروید و آی او اس به صورت Native کد خود را به زبان C# نوشته و آنها را اجرا کنند.
چرا باید از Xamarin استفاده کنم؟
در ادامه مقاله قصد دارم شما را با برخی از ویژگیهای زامارین آشنا کرده و مزایای استفاده از آن را بیان کنم.
Xamarin امکانی را فراهم کردهاست که برنامه نویسان به دو روش متفاوت قادر خواهند بود برنامههای خود را بنویسند:
Xamarin Native :1
زامارین به شما این امکان را میدهد که بتوانید به صورت مستقیم برای هر پلتفرم به صورت جداگانه برنامه نویسی کنید. در اندروید اینکار با Xamarin.Droid و در IOS اینکار با Xamarin.Touch امکانپذیر است. مزیتهای استفاده از این روش عبارتند از:
· بهره گیری از یک زبان برنامه نویسی
همانطور که میدانید یادگیری یک زبان برنامه نویسی هزینهی زیادی را برای یک سازمان و یا یک شخص به همراه دارد. در زامارین این امکان فراهم شدهاست که با استفاده از تنها یک زبان برنامه نویسی مانند C# ، برنامه نویسان بتوانند برای پلتفرمهای مختلف برنامه بنویسند. در نظر داشته باشید که UI در هر پلتفرم به صورت جداگانه پیاده سازی میشود. به طور مثال در اندروید به وسیلهی Android Xml میتوانید ظاهر برنامه خود را پیاده سازی کنید و منطقهای خود را با زبان C# برای تمامی پلت فرمها به صورت یکسان بنویسید.
· تجربه کاربری Native
زامارین به شما این امکان را خواهد داد که با استفاده از کنترلهای Native هر پلتفرم به تجربه کاربری همان پلت فرم دسترسی پیدا کنید و اپلیکیشنی با ظاهر و UX همان پلتفرم بسازید.
· استفاده 100% از امکانات هر پلتفرم
زامارین به دلیل Native بودن این امکان را به برنامه نویسان ارائه میدهد که با استفاده از یک زبان و با بکارگیری Cycle Life مخصوص هر پلتفرم، به 100% امکانات و API های هر پلتفرم دسترسی پیدا کنند.
· Performance
به دلیل اینکه برنامههای زامارین به صورت Native اجرا میشوند Performance بالایی دارند.
· دسترسی به API های موجود در .Net
شما قادر خواهید بود با دانش موجود مانند Entity Framework Code و.. به فریم ورک .Net دسترسی پیدا کرده و از API های درون آن استفاده کنید. زامارین از یکی از پیاده سازیهای .Net Standard استفاده میکند.
· استفاده مجدد از کد
در زامارین قادر خواهید بود که از کدهای خود، استفاده مجدد کرده و این امر سبب مدیریت بهتر بر روی کد، کد نویسی کمتر، هزینه نگهداری کد کمتر، توسعه راحتتر اپلیکیشن و ... میشود.
· تست خودکار
در زامارین شما میتوانید برای کدهای خود تست خودکار نوشته و آنها را به صورت خودکار تست کنید.
· Bind کردن Library های Objective-C و Java
زامارین طوری طراحی شدهاست که دست شما را در هیچگونه شرایطی نخواهد بست. شما میتوانید به صورت مستقیم کدهایی را که به زبان های Java و Objective-C نوشته شدهاند، به پروژه اضافه کرده و هیچگونه نگرانی از بابت کدهای از قبل نوشته شده که به زبانهای Objective-C و Java هستند، نداشته باشید.
· Designer
در زمارین این امکان وجود دارد که در هر پلتفرم از طریق Designer مخصوص به آن پلتفرم، UI خود را طراحی و پیاده سازی کنید.
· Async
در برنامه نویسی غیر همزمان ( Asynchronous Programming ) این امکان وجود دارد که برنامه شما بدون توقف، یک قسمت از کد را اجرا کرده و منتظر اجرای قسمتهای دیگر کد نشود؛ یا به اصطلاح برنامه از حالت Response خارج نشود. در زبانهای Java ، Objective-C و Swift اینکار باید با CallBack و به صورت Manual مدیریت شود؛ اما #C این امکان را فراهم آورده است که به راحتی اینکار را انجام داده و برنامه خود را همیشه در حالت پاسخ دهی نگه دارید.
public async Task<List<FeedItem>> GetFeedItems(DateTime date) { var feed = "http://planet.xamarin.com/feed/"; var response = await httpClient.GetStringAsync(feed); var items = await ParseFeedAsync(response); return items.Where(item => item.Published.Date == date).ToList(); }
· Parallel Programming
در برنامه نویسی موازی( Parallel Programming ) برخلاف برنامه نویسی MultiThread که بر روی یک هسته CPU اجرا میشود، بر روی چندین هسته CPU به صورت موازی اجرا میشود. زامارین از این نوع برنامه نویسی پشتیبانی میکند.
Xamarin.Forms: 2
پس از معرفی Xamarin Forms API شما میتوانید علاوه بر مزیتهایی که در بالا اشاره شد، کدهای Logic خود را با زبان C# و کدهای UI خود را با زبان XAML پیاده سازی کرده و با یک بار نوشتن کد، در پلتفرمهای مختلف خروجی خود را مشاهده کنید. مزیت استفاده از Xamarin Forms عبارتند از:
· استفاده از کد واحد برای پیاده سازی UI و Logic
یکی از بهترین مزیتهایی را که میتوان به آن اشاره نمود این است که شما کافیست یک بار کد خود را بنویسید و Xamarin Forms کد شما را در پلت فرمهای متفاوت پیاده سازی خواهد کرد.
<?xml version="1.0" encoding="UTF-8"?> <TabbedPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="MyApp.MainPage"> <TabbedPage.Children> <ContentPage Title="Profile" Icon="Profile.png"> <StackLayout Spacing="20" Padding="20" VerticalOptions="Center"> <Entry Placeholder="Username" Text="{Binding Username}"/> <Entry Placeholder="Password" Text="{Binding Password}" IsPassword="true"/> <Button Text="Login" TextColor="White" BackgroundColor="#77D065" Command="{Binding LoginCommand}"/> </StackLayout> </ContentPage> <ContentPage Title="Settings" Icon="Settings.png"> <!-- Settings --> </ContentPage> </TabbedPage.Children> </TabbedPage>
برای پیاده سازی UI در Xamarin Forms باید از XAML استفاده کنید. همچنین مانند روش قبلی میتوانید از زبان C# برای پیاده سازی منطق، استفاده نمایید.
با استفاده از Xamarin Forms شما تجربه نوشتن کد Cross Platform را در کنار Native بودن آن خواهید داشت.
· استفاده از یک کنترل مخصوص یک پلتفرم در بین کد ( Embedding )
در Xamarin Forms این امکان وجود دارد که اگر شما خواستید یک کنترل مخصوص IOS را در بین کدهای خود استفاده کنید، بتوانید به راحتی اینکار را انجام دهید.( (Embedding
اگر به تصویر بالا دقت کنید متوجه خواهید شد که در یکسری از کنترلها، تصاویر متفاوت هستند. در نسخه اندروید یک Action Button در قسمت پایین صفحه مشاهده میکنید که در نسخهی IOS آن موجود نیست. یعنی به صورت مستقیم کنترل Action Button که مختص به پلت فرم اندروید میباشد، درون Xamarin Forms استفاده شده است.
· دسترسی به هر پلتفرم به طور مستقیم
شما قادر خواهید بود به طور مستقیم به هر پلت فرم دسترسی پیدا کرده و به طور مثال در هر پلتفرم، UI مخصوص به خود را با Property های مخصوص به خود طراحی کنید.
· UITest و Test Cloud
در Xamarin Forms میتوانید برای UI خود تست نوشته و آنها را به وسیله Xamarin Test Cloud بر روی صدها Device متفاوت تست کنید. (این امکان فقط برای Android و IOS وجود دارد.)
· Life Cycle مشابه در تمامی پلتفرم ها
همانطور که میدانید پلتفرمهای مختلف، Life Cycle های متفاوتی برای مدیریت اپلیکیشن دارند. یکی از مزیتهای استفاده از Xamarin Forms این است که شما میتوانید برای تمامی پلتفرمها بهوسیلهی یک Life Cycle یکسان کد بنویسید.
· Previewer
یکی از بهترین قابلیتهایی که در Xamarin Forms اضافه شدهاست این است که شما قادر خواهید بود به صورت Real Time خروجی فایل XAML خود را به وسیله Previewer مشاهده نمایید.
· Performance Profiler
به وسیله Xamarin Profiler شما میتوانید میزان مصرف حافظه، Performance و ... را در اندروید و IOS اندازه گیری نمایید.
نکات قابل توجه:
Ø استفاده همزمان از Xamarin Forms و Xamarin Native
شما میتوانید کدهای خود را با حداکثر Reusability نوشته و در صورت لزوم با کدهای Xamarin Native ترکیب کنید.
Ø Documentation خیلی خوب
زمارین مستندات جامع و کاملی را در سایت خود گردآوری کرده که میتوانید به راحتی از آن برای فهم تمامی قسمتهای Xamarin استفاده کنید.