آموزشگاه برنامه نویسی تحلیل داده
آموزشگاه برنامه نویسی تحلیل داده

آموزش ساخت Data Model برای اپلیکیشن های IOS

ساخت Data Model برای اپلیکیشن

در این مبحث، یک data model برای اپلیکیشن FoodTracker تعریف خواهید کرد. Data model داده های اپلیکیشن را در خود ذخیره کرده و نمای درختی از آن ها را ارائه می دهد.

آنچه خواهید آموخت:
  • یک data model برای اپلیکیشن خود ایجاد نمایید.
  • پیاده سازی failable initializer در کلاس اختصاصی خود.
  • تفاوت مفهومی بین initializer های failable و nonfailable را تشریح کنید.
  • Data model را با (نوشتن و اجرای) unit test آزمایش کرده و از کارکرد صحیح آن اطمینان حاصل نمایید.

ساخت data model

اکنون یک data model ایجاد می کنید که داده های اپلیکیشن را در خود ذخیره کرده و اطلاعاتی که scene (صفحه ی محتوای برنامه) جاری برای کاربر در UI به نمایش می گذارد را فراهم می نماید. برای این منظور، در ابتدا یک کلاس با سه property جهت ذخیره اطلاعات مربوط به اسم، عکس و امتیاز غذا تعریف می کنید. به منظور ایجاد یک کلاس data model جهت ذخیره ی اطلاعات برنامه، مراحل زیر را دنبال نمایید:

  1. ابتدا این مسیر را طی نمایید: File > New > File یا کلیدهای Command-N را همزمان فشار دهید تا فایل جدید ایجاد شود.
  2. در سمت چپ کادر محاوره ای که نمایان می شود، گزینه ی Source را از زیر iOS انتخاب نمایید.
  3. Swift File را انتخاب نموده و بر روی دکمه ی Next کلیک نمایید.فرایدی که برای ایجاد کلاس data model طی می کنید از پروسه ی ایجاد کلاسی که برای پیاده سازی کنترل امتیازدهی (کلاس RatingControl) دنبال کردید (iOS > Source > Cocoa Touch Class) کاملا متفاوت است چرا که data model یک کلاس پایه است که از کلاس دیگری ارث بری ندارد. اما اگر بخاطر داشته باشید کلاس RatingControl از کلاس UIView مشتق می شد.
  4. داخل فیلد Save As، واژه ی Meal را به عنوان اسم فایل جدید انتخاب نمایید.
  5. فایل مورد نظر به صورت پیش فرض بر روی پوشه ی پروژه ذخیره می شود (save location به صورت پیش فرض بر روی دایرکتوری پروژه تنظیم می شود). فیلد Group به صورت پیش فرض بر روی اسم اپلیکیشن، FoodTracker، تنظیم می شود. در بخش Targets، لازم است هر دو گزینه را فعال نمایید (کادر تیک دوم مربوط به تست های برنامه می باشد که باید برای این مبحث آن را انتخاب نمایید).
    ساخت Data Model برای اپلیکیشن IOS
  6. بر روی دکمه ی Create کلیک نمایید. Xcode یک فایل به نام Meal.swift ایجاد می کند. اسم غذا را می توانید در یک متغیر از نوع String، عکس را در قالب یک متغیر از جنس کلاس UIImage و امتیاز غذا را با استفاده از یک عدد صحیح یا Int ذخیره نمایید. از آنجایی که یک غذا همیشه یک اسم و امتیاز دارد، اما ممکن است عکس نداشته باشد، می توانید متغیر سوم را به وسیله ی عملگر " ? "، optional تعریف کنید (بدین معنی که در صورت عدم وجود عکس بتواند مقدار nil بپذیرد).
جهت تعریف data model برای ذخیره و نگهداری اطلاعات مربوط به غذا، مراحل زیر را به ترتیب دنبال نمایید:

1.در صورت باز بودن assistant editor، با کلیک بر روی دکمه ی Standard editor (اشاره شده در تصویر زیر)، ویرایشگر اصلی محیط کاری Xcode را با نمایید:
 Data Model برای اپلیکیشن IOS
2.حال فایل Meal.swift را باز نمایید.
3.دستور import را جهت وارد کردن کتابخانه ی UIKit به صورت زیر ویرایش نمایید:

import UIKit

به صورت پیش فرض، یک فایل Swift چارچوب نرم افزاری/framework Foundation را وارد پروژه می کند تا شما بتوانید با ساختار های داده ای نظیر آرایه کار کنید. در این مبحث از کلاس های چارچوب نرم افزاری UIKit استفاده خواهید کرد، بنابراین لازم است UIKit را در دستور import در بالای فایل لحاظ نمایید. لازم به ذکر است که UIKit، علاوه بر کلاس های خود، امکان دسترسی به کلاس های Foundation را نیز فراهم می نماید (از این جهت بهتر است دستور اضافی import Foundation را از فایل حذف کنید).
4.در زیر دستور import، کد زیر را درج نمایید:

 class Meal
{
   // MARK: Properties
   var name: String
   var photo: UIImage?
   var rating: Int
}

این کد property هایی (متغیر) تعریف می کند که نقش ظرف را برای داده های مورد نیاز شما ایفا کرده و آن ها را در خود نگه می دارند. همان طور که می بینید برای تعریف property ها، بجای let از کلیدواژه ی var استفاده شده چرا که ممکن است اطلاعات ذخیره شده در آن، در آینده تغییر کند (یا به عبارتی طی چرخه ی حیات آبجکت Meal مقدارشان عوض شود).
5. اکنون در زیر property های مزبور، این کد را جهت تعریف یک initializer (متد سازنده/مقداردهنده ی اولیه) اضافه نمایید:

  // MARK: Initialization
  init(name: String, photo: UIImage?, rating: Int) {
}
            

یادآور می شویم که initializer یک نمونه از روی کلاس جاری ساخته، آن را برای استفاده آماده می کند که مقداردهی اولیه ی property های کلاس و انجام دیگر تنظیمات لازم جزئی از این آماده سازی، محسوب می شود.
6.بدنه ی این متد را به صورت زیر پیاده سازی نمایید (مقدار پارامترهای ارسال شده به متد سازنده را برابر property های کلاس قرار دهید):

                // Initialize stored properties.
                self.name = name
                self.photo = photo
                self.rating = rating
            

اینجا یک سوال جالب مطرح می شود: اگر سعی کنید یک آبجکت Meal با مقادیر غیر مجاز نظیر امتیاز منفی ایجاد کنید یا فیلد name را خالی بگذارید، چه اتفاقی می افتد؟ در چنین سناریویی بایستی مقدار nil را به نشانه ی اینکه امکان ایجاد آیتم مورد نظر وجود نداشته و فیلدها به ناچار با همان مقادیر پیش فرض مقدار دهی شده اند، از متد سازنده (initialzer) برگردانید. برای نیل به این هدف می بایست کدی (یک متد سازنده با دستور شرطی if در بدنه ی آن) اضافه کنید که ابتدا این دست سناریوها را بررسی کرده و در صورتی که در مقداردهی اولیه ی property های آبجکت با شکست مواجه شد، مقدار nil را در خروجی برگرداند.
7.در انتهای پیاده سازی متد سازنده (initializer)، یک دستور شرطی if اضافه می کنید که مقادیر را بررسی کرده و در صورت برخورد با مقدار غیرمجاز، nil را به عنوان خروجی بازمی گرداند.

            // Initialization should fail if there is no name or if the rating is negative.
            اگر مقداری برای فیلد ِنِیم ارائه نشده باشد یا مقدار امتیاز برای آبجکت منفی وارد شود، مقداردهی اولیه قاعدتا باید با شکست مواجه شود//
            if name.isEmpty || rating < 0 {
            return nil}
            }
            

از آنجایی که متد سازنده (initializer) ممکن است در فرایند مقداردهی اولیه با شکست مواجه شده و مقدار nil را در خروجی برگرداند، این امر را باید در signature (خط تعریف) متد سازنده مشخص کنید.
8.با کلیک بر روی آیکون قرمز رنگ fix-it (راه حلی که کامپایلر در پاسخ به خطا در کدنویسی شما ارائه می دهد) عملگر "؟" را به تعریف initializer اضافه نمایید.
 Data Model برای اپلیکیشن IOS

	init?(name: String, photo: UIImage?, rating: Int) {

متد سازنده ای (initializer) که به این صورت (با علامت سوال) نگارش می شود در اصطلاح failable initializer نام دارد. این متد قادر است در صورت عدم موفقیت در فرایند مقداردهی اولیه property های کلاس، nil را در خروجی برگرداند. در حال حاضر، متد سازنده ی init?(name:photo:rating:) می بایست ظاهری مشابه زیر داشته باشد:

            // MARK: Initialization
            init?(name: String, photo: UIImage?, rating: Int) {
            // Initialize stored properties.
            مقداردهی اولیه ی متغیرهای کلاس//
            self.name = name
            self.photo = photo
            self.rating = rating
            // Initialization should fail if there is no name or if the rating is negative.
            در صورتی که مقداردهی اولیه با شکست مواجه شد، برای مثال مقدار ریتینگ منفی بود، باید در خروجی مقدار نیل برگرداند/مقداردهی اولیه با شکست مواجه شود//
            if name.isEmpty || rating < 0 {
            return nil
            }
            }
            
تست کنید:

پروژه ی خود را کامپایل نمایید (Product > Build یا فشردن همزمان کلیدهای Command-B). در حال حاضر کلاس Meal را برای منظور خاصی بکار نمی برید، بلکه با build آن، صرفا کامپایلر را از عدم وجود خطا در کد خود مطمئن می سازید (برای مثال به کامپایلر اعلان می کنید که initializer را با درج ؟ از نوع failable تعریف کردید و در کد خطای نگارشی وجود ندارد). اگر در کدنویسی شما خطایی وجود داشت، در آن صورت می بایست هشدارهایی که کامپایلر در خصوص بخش های مختلف کد ارائه می دهد را خوانده، سپس دستورالعمل های تشریح شده در مبحث جاری را مرور نمایید و کد را مطابق با آن ویرایش کنید.

تست کردن داده ها با استفاده از unit test

گرچه کد data model شما با موفقیت کامپایل می شود، اما هنوز آن را به طور کامل در اپلیکیشن خود جاسازی نکرده اید. در نتیجه، نمی توان با اطمینان کامل گفت که همه چیز را به درستی پیاده سازی کردید. همچنین ممکن است در زمان اجرا با مشکلات و شرایطی مواجه شوید که انتظارش را ندارید (به عنوان مثال ورودی کاربر از حداکثر مقدار مجاز تعیین شده توسط برنامه بیشتر یا کمتر باشد. مانند زمانی که به یک غذا مقدار منفی داده می شود). برای مدیریت این عدم قطعیت و کسب اطمینان از کارکرد صحیح برنامه، می توانید برای کد خود unit test بنویسید. Unit test عبارت است از آزمایش قطعه کدهای کوچک و مستقل برنامه (مجزا از دیگر بخش های برنامه) جهت اطمینان حاصل نمودن از عملکرد صحیح آن ها. کلاس Meal گزینه ی مناسبی برای اجرای unit test می باشد. محیط Xcode خود یک فایل unit test به عنوان بخشی از فایل الگو (template) Single View Application ایجاد کرده و به صورت آماده در اختیار شما قرار می دهد. به منظور مشاهده و دسترسی به فایل unit test جهت اپلیکیشن FoodTracker، مراحل زیر را گام به گام دنبال نمایید:
1. پوشه ی FoodTrackerTests را در project navigator با کلیک بر روی آیکون مثلثی که در کنار آن به نمایش گذاشته شده، باز نمایید:
 Data Model برای اپلیکیشن IOS
2. فایل FoodTrackerTests.swift را باز نمایید.
حال زمان مختصری را به درک کد موجود در این فایل تخصیص دهید.

                
import UIKit
import XCTest
class FoodTrackerTests: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testExample() {
// This is an example of a functional test case.
XCTAssert(true, "Pass")
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measureBlock() {
// Put the code you want to measure the time of here.
}
}
}

Xcode یک فریم ورک تست گیری به نام XCTest دارد که به واسطه ی دستور import XCTest، این فریم ورک تست گیری را جهت آزمایش بخش های مختلف برنامه در فایل جاری وارد می کنید. unit test ها (توابع تست گیری) در سطح کلاسی به نام FoodTrackerTests تعریف می شوند که این کلاس خود اعضایش را از کلاس XCTestCase به ارث می برد. همان طور که در کد بالا مشاهده می کنید، comment هایی در بالای دو متد setUp() و tearDown() درج شده که توضیحاتی درباره ی کاربرد آن ها ارائه می دهد. در کل دو نوع unit test وجود دارد: 1. تست های functional که جهت تست قسمت های مختلف برنامه به صورت مجزا، نوشته می شوند (به طوری که تغییر در بخش های دیگر را به دنبال نداشته باشد و به بخش های دیگر وابسته نباشد). به عبارت دیگر، به واسطه ی این نوع تست عملکرد کامپوننت های مختلف نرم افزار را آزمایش می کنید و مطمئن می شوید رفتار تعریف شده و عملیات مورد انتظار را پیاده سازی می کند یا خیر 2. تست های performance که جهت بررسی سرعت اجرا و کارایی کد تنظیم می شوند (کد با سرعت مورد انتظار اجرا می شود یا خیر). تا اینجای آموزش هیچ عملیات سنگینی ننوشته ایم که نیاز به تست سرعت اجرا و کارایی آن داشته باشیم، از اینرو فعلا فقط به نوشتن تست های functional بسنده می کنیم. بهتر است عنوان متدهای تست گیری خود را با واژه ی“test” آغاز کرده و باقی آن را نیز طوری انتخاب کنید که فهم و شناسایی کاربرد آن بعده ها برای شما آسان باشد. به عنوان مثال، می توانید یک تست ساده بنویسید که بررسی می کند آیا property های کلاس Meal به درستی مقداردهی اولیه می شوند یا خیر و اسم این تست را testMealInitialization انتخاب نمایید. برای نوشتن تستی که صحت مقداردهی اولیه ی property های آبجکت Meal را بررسی می کند، مراحل زیر را گام به گام دنبال نمایید:
1. از داخل فایل FoodTrackerTests.swift، تست های الگو (template) را حذف نمایید.

import UIKit
import XCTest
class FoodTrackerTests: XCTestCase {
}

برای تست عملکرد کدهای این برنامه به هیچ یک از پیاده سازی های الگویی (template implementation) که محیط Xcode به صورت آماده در اختیار شما قرار می دهد، نیاز ندارید و می توانید آن ها را حذف نمایید.
2. به قبل از آخرین ({)، کد زیر را اضافه نمایید:

                3.	// MARK: FoodTracker Tests

این خط صرفا یک comment است که به وسیله ی آن شرح مختصری درباره ی کاربرد قسمت های مختلف تست و اینکه هر تست برای آزمایش کدام بخش از برنامه نوشته شده است، ارائه می دهید.
4. حال در زیر این comment، یک unit test جدید به صورت زیر اضافه نمایید:

                             
// Tests to confirm that the Meal initializer returns when no name or a negative rating is provided.
func testMealInitialization() {
}

5. ابتدا یک مورد تست (test case) اضافه نمایید که مطمئنید با موفقیت اجرا می شود. comment و دستورات زیر را به متد testMealInitialization() اضافه نمایید.

// Success case.
let potentialItem = Meal(name: "Newest meal", photo: nil, rating: 5)
XCTAssertNotNil(potentialItem)

6. XCTAssertNotNil تست کرده و مطمئن می شود که آبجکت Meal پس از پروسه ی مقداردهی اولیه، nil نباشد. این بدین معنی است که متد سازنده (initializer) توانسته با موفقیت یک آبجکت Meal با مقادیر ارائه شده به عنوان پارامتر، ایجاد کند.
7. اکنون یک مورد تست اضافه نمایید که در آن مقداردهی اولیه ی آبجکت Meal مطمئنا با شکست مواجه می شود. comment و دستورات زیر را به متد (تست گیری) testMealInitialization() اضافه نمایید:

// Failure cases.
let noName = Meal(name: "", photo: nil, rating: 0)
XCTAssertNil(noName, "Empty name is invalid")

XCTAssertNil انتظار دارد (assert) که آبجکت noName دارای مقدار nil باشد، بدین معنی که پروسه ی مقداردهی اولیه که توسط متد initializer باید انجام شود با شکست مواجه می شود. به عبارت دیگر شما انتظار دارید که مقداردهی اولیه ناموفق باشد چرا که پارامتر name یک رشته ی تهی است.
8. اکنون یک مورد تست دیگر اضافه کنید که در آن همواره آبجکت Meal در مقداردهی اولیه با شکست مواجه می شود، اما این بار (با استفاده از متد XCTAssertNotNil) assert کنید که مقداردهی اولیه باید با موفقیت انجام شود. برای این منظور کد زیر را به متد تست گیری testMealInitialization() اضافه نمایید:

let badRating = Meal(name: "Really bad rating", photo: nil, rating: -1)
XCTAssertNotNil(badRating)

شما انتظار دارید که این مورد تست نیز ناموفق باشد زیرا مقدار پارامتر rating منفی است.
در حال حاضر بدنه ی متد تست گیری testMealInitialization()می بایست شبیه نمونه ی زیر باشد:

// Tests to confirm that the Meal initializer returns when no name or a negative rating is provided.
func testMealInitialization() {
// Success case.
let potentialItem = Meal(name: "Newest meal", photo: nil, rating: 5)
XCTAssertNotNil(potentialItem)
// Failure cases.
let noName = Meal(name: "", photo: nil, rating: 0)
XCTAssertNil(noName, "Empty name is invalid")
let badRating = Meal(name: "Really bad rating", photo: nil, rating: -1)
XCTAssertNotNil(badRating)
}

می توانید با فشردن کلیدهای Command-U تمامی تست های خود را به طور همزمان اجرا نمایید و یا هر یک را به صورت مجزا اجرا کنید. آخرین تست باید با شکست مواجه شود چرا که آبجکت در اصل دارای مقدار منفی است و اگر بخاطر داشته باشید با استفاده از متد XCTAssertNotNil انتظار داشتید که مقدار nil نباشد، در حالی که در اصل nilو فاقد مقدار مجاز است (در failable initializer شرطی اضافه کردیم که در آن مقدار متغیر rating نمی توانست منفی باشد).
به منظور اجرای متد تست گیری testMealInitialization()، مراحل زیر را دنبال نمایید:
1. در فایل FoodTrackerTests.swift، متد testMealInitialization() را پیدا کنید.
2. در سمت چپ اسم متد (تست گیری)، شکل لوزی را پیدا کنید.

 Data Model برای اپلیکیشن IOS
3. اشاره گر موس را بر روی شکل لوزی معلق نگه داشته تا دکمه ی اجرا به نمایش در آید.
 Data Model برای اپلیکیشن IOS
تست کنید:

اپلیکیشن را تست کنید. برنامه باید با تستی که شما نوشته اید، اجرا شود. انتظار می رود که دو نمونه ی اول با موفقیت انجام (pass) شوند، اما سومین باید ناموفق باشد (fail).

 Data Model برای اپلیکیشن IOS

همان طور که می بینید unit test به شما کمک می کند خطاها را در کد خود پیدا و متعاقبا مدیریت کنید. اگر واقعا انتظار داشتیند که آبجکت Meal دارای مقدار یا non-nil باشد، آنگاه این خطا را در طول فرایند تست گیری از برنامه ی خود پیدا می کردید (در اینجا از قصد یک مورد تست غلط نوشتید، به همین دلیل به بخش مربوطه مراجعه کرده و به سادگی آن را برطرف می کنید.)
حال جهت برطرف کردن خطای مورد تست، مراحل زیر را دنبال نمایید:
1. در فایل FoodTrackerTests.swift، متد تست گیری testMealInitialization() را پیدا کنید.
2. آخرین خط کد را به صورت زیر ویرایش کنید:

        	XCTAssertNil(badRating, "Negative ratings are invalid, be positive")
متد تست گیری testMealInitialization() اکنون می بایست دارای پیاده سازی زیر باشد:
// Tests to confirm that the Meal initializer returns when no name or a negative rating is provided.
func testMealInitialization() {
// Success case.
let potentialItem = Meal(name: "Newest meal", photo: nil, rating: 5)
XCTAssertNotNil(potentialItem)
// Failure cases.
let noName = Meal(name: "", photo: nil, rating: 0)
XCTAssertNil(noName, "Empty name is invalid")
let badRating = Meal(name: "Really bad rating", photo: nil, rating: -1)
XCTAssertNil(badRating, "Negative ratings are invalid, be positive")
}
تست کنید :

برنامه ی شما با تستی که هم اکنون نوشتید اجرا می شود. این بار تمامی موارد تست باید با موفقیت اجرا شوند (پاس شوند). می توان گفت که Unit test یک بخش اساسی و جدایی ناپذیر از کدنویسی و روش های صحیح ساخت اپلیکیشن است چرا که به شما کمک می کند خطاهایی که ممکن است در صورت عدم اجرای unit test نادیده بگیرید را در متن برنامه ی (source code) خود پیدا کرده و مدیریت نمایید. همان طور که از نامش پیدا است، unit test باید ماژولار باشد. به عبارت دیگر، بهتر است برای هر بخش از کد خود یک unit test مجزا تنظیم نمایید. در واقع هر تست می بایست رفتار فقط یک بخش از برنامه را تست کند. اگر unit test هایی بنویسید که بیش از حد طولانی یا پیچیده باشند، آنگاه یافتن خطا و اینکه کجای برنامه اشکال دارد، به مراتب دشوارتر می شود.

  • 1521
  •    1100
  • تاریخ ارسال :   1395/08/03

دانلود PDF دانشجویان گرامی اگر این مطلب برای شما مفید بود لطفا ما را در GooglePlus محبوب کنید
رمز عبور: tahlildadeh.com یا www.tahlildadeh.com
ارسال دیدگاه نظرات کاربران
شماره موبایل دیدگاه
عنوان پست الکترونیک

ارسال

آموزشگاه برنامه نویسی تحلیل داده
آموزشگاه برنامه نویسی تحلیل داده

تمامی حقوق این سایت متعلق به آموزشگاه تحلیل داده می باشد .