age mishe linke matlabi ro dar morede sakhte feed rss baraye site neveshte shode ba c# va mssql , va php va mysql bedin chon man kheyli vagte donbalesh migardam vali matlabe khoobi peyda nemikonam va chon poste shomaro khundam dardham taze shod
mamnoo az tavajohetoon
pishapish
مباحث eager fetching/loading (واکشی حریصانه) و lazy loading/fetching (واکشی در صورت نیاز، با تاخیر، تنبل) جزو نکات کلیدی کار با ORM های پیشرفته بوده و در صورت عدم اطلاع از آنها و یا استفادهی ناصحیح از هر کدام، باید منتظر از کار افتادن زود هنگام سیستم در زیر بار چند کاربر همزمان بود. به همین جهت تصور اینکه "با استفاده از ORMs دیگر از فراگیری SQL راحت شدیم!" یا اینکه "به من چه که پشت صحنه چه اتفاقی میافته!" بسی مهلک و نادرست است!
در ادامه به تفصیل به این موضوع پرداخته خواهد شد.
ابزار مورد نیاز
در این مطلب از برنامهی NHProf استفاده خواهد شد.
اگر مطالب NHibernate این سایت را دنبال کرده باشید، در مورد لاگ کردن SQL تولیدی به اندازهی کافی توضیح داده شده یا حتی یک ماژول جمع و جور هم برای مصارف دم دستی نوشته شده است. این موارد شاید این ایده را به همراه داشته باشند که چقدر خوب میشد یک برنامهی جامعتر برای این نوع بررسیها تهیه میشد. حداقل SQL نهایی فرمت میشد (یعنی برنامه باید مجهز به یک SQL Parser تمام عیار باشد که کار چند ماهی هست ...؛ با توجه به اینکه مثلا NHibernate از افزونههای SQL ویژه بانکهای اطلاعاتی مختلف هم پشتیبانی میکند، مثلا T-SQL مایکروسافت با یک سری ریزه کاریهای منحصر به MySQL متفاوت است)، یا پس از فرمت شدن، syntax highlighting به آن اضافه میشد، در ادامه مشخص میکرد کدام کوئریها سنگینتر هستند، کدامیک نشانهی عدم استفادهی صحیح از ORM مورد استفاده است، چه مشکلی دارد و از این موارد.
خوشبختانه این ایدهها یا آرزوها با برنامهی NHProf محقق شده است. این برنامه برای استفادهی یک ماه اول آن رایگان است (آدرس ایمیل خود را وارد کنید تا یک فایل مجوز رایگان یک ماهه برای شما ارسال گردد) و پس از یک ماه، باید حداقل 300 دلار هزینه کنید.
واکشی حریصانه و غیرحریصانه چیست؟
رفتار یک ORM جهت تعیین اینکه آیا نیاز است برای دریافت اطلاعات بین جداول Join صورت گیرد یا خیر، واکشی حریصانه و غیرحریصانه را مشخص میسازد.
در حالت واکشی حریصانه به ORM خواهیم گفت که لطفا جهت دریافت اطلاعات فیلدهای جداول مختلف، از همان ابتدای کار در پشت صحنه، Join های لازم را تدارک ببین. در حالت واکشی غیرحریصانه به ORM خواهیم گفت به هیچ عنوان حق نداری Join ایی را تشکیل دهی. هر زمانی که نیاز به اطلاعات فیلدی از جدولی دیگر بود باید به صورت مستقیم به آن مراجعه کرده و آن مقدار را دریافت کنی.
به صورت خلاصه برنامه نویس در حین کار با ORM های پیشرفته نیازی نیست Join بنویسد. تنها باید ORM را طوری تنظیم کند که آیا اینکار را حتما خودش در پشت صحنه انجام دهد (واکشی حریصانه)، یا اینکه خیر، به هیچ عنوان SQL های تولیدی در پشت صحنه نباید حاوی Join باشند (lazy loading).
چگونه واکشی حریصانه و غیرحریصانه را در NHibernate 3.0 تنظیم کنیم؟
در NHibernate اگر تنظیم خاصی را تدارک ندیده و خواص جداول خود را به صورت virtual معرفی کرده باشید، تنظیم پیش فرض دریافت اطلاعات همان lazy loading است. به مثالی در این زمینه توجه بفرمائید:
مدل برنامه:
مدل برنامه همان مثال کلاسیک مشتری و سفارشات او میباشد. هر مشتری چندین سفارش میتواند داشته باشد. هر سفارش به یک مشتری وابسته است. هر سفارش نیز از چندین قلم جنس تشکیل شده است. در این خرید، هر جنس نیز به یک سفارش وابسته است.
using System.Collections.Generic;
namespace CustomerOrdersSample.Domain
{
public class Customer
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Order> Orders { get; set; }
}
}
using System;
using System.Collections.Generic;
namespace CustomerOrdersSample.Domain
{
public class Order
{
public virtual int Id { get; set; }
public virtual DateTime OrderDate { set; get; }
public virtual Customer Customer { get; set; }
public virtual IList<OrderItem> OrderItems { set; get; }
}
}
namespace CustomerOrdersSample.Domain
{
public class OrderItem
{
public virtual int Id { get; set; }
public virtual Product Product { get; set; }
public virtual int Quntity { get; set; }
public virtual Order Order { set; get; }
}
}
namespace CustomerOrdersSample.Domain
{
public class Product
{
public virtual int Id { set; get; }
public virtual string Name { get; set; }
public virtual decimal UnitPrice { get; set; }
}
}
که جداول متناظر با آن به صورت زیر خواهند بود:
create table Customers (
CustomerId INT IDENTITY NOT NULL,
Name NVARCHAR(255) null,
primary key (CustomerId)
)
create table Orders (
OrderId INT IDENTITY NOT NULL,
OrderDate DATETIME null,
CustomerId INT null,
primary key (OrderId)
)
create table OrderItems (
OrderItemId INT IDENTITY NOT NULL,
Quntity INT null,
ProductId INT null,
OrderId INT null,
primary key (OrderItemId)
)
create table Products (
ProductId INT IDENTITY NOT NULL,
Name NVARCHAR(255) null,
UnitPrice NUMERIC(19,5) null,
primary key (ProductId)
)
alter table Orders
add constraint fk_Customer_Order
foreign key (CustomerId)
references Customers
alter table OrderItems
add constraint fk_Product_OrderItem
foreign key (ProductId)
references Products
alter table OrderItems
add constraint fk_Order_OrderItem
foreign key (OrderId)
references Orders
همچنین یک سری اطلاعات آزمایشی زیر را هم در نظر بگیرید: (بانک اطلاعاتی انتخاب شده SQL CE است)
SET IDENTITY_INSERT [Customers] ON;
GO
INSERT INTO [Customers] ([CustomerId],[Name]) VALUES (1,N'Customer1');
GO
SET IDENTITY_INSERT [Customers] OFF;
GO
SET IDENTITY_INSERT [Products] ON;
GO
INSERT INTO [Products] ([ProductId],[Name],[UnitPrice]) VALUES (1,N'Product1',1000.00000);
GO
INSERT INTO [Products] ([ProductId],[Name],[UnitPrice]) VALUES (2,N'Product2',2000.00000);
GO
INSERT INTO [Products] ([ProductId],[Name],[UnitPrice]) VALUES (3,N'Product3',3000.00000);
GO
SET IDENTITY_INSERT [Products] OFF;
GO
SET IDENTITY_INSERT [Orders] ON;
GO
INSERT INTO [Orders] ([OrderId],[OrderDate],[CustomerId]) VALUES (1,{ts '2011-01-07 11:25:20.000'},1);
GO
SET IDENTITY_INSERT [Orders] OFF;
GO
SET IDENTITY_INSERT [OrderItems] ON;
GO
INSERT INTO [OrderItems] ([OrderItemId],[Quntity],[ProductId],[OrderId]) VALUES (1,10,1,1);
GO
INSERT INTO [OrderItems] ([OrderItemId],[Quntity],[ProductId],[OrderId]) VALUES (2,5,2,1);
GO
INSERT INTO [OrderItems] ([OrderItemId],[Quntity],[ProductId],[OrderId]) VALUES (3,20,3,1);
GO
SET IDENTITY_INSERT [OrderItems] OFF;
GO
دریافت اطلاعات :
میخواهیم نام کلیه محصولات خریداری شده توسط مشتریها را به همراه نام مشتری و زمان خرید مربوطه، نمایش دهیم (دریافت اطلاعات از 4 جدول بدون join نویسی):
var list = session.QueryOver<Customer>().List();
foreach (var customer in list)
{
foreach (var order in customer.Orders)
{
foreach (var orderItem in order.OrderItems)
{
Console.WriteLine("{0}:{1}:{2}", customer.Name, order.OrderDate, orderItem.Product.Name);
}
}
}
خروجی به صورت زیر خواهد بود:
Customer1:2011/01/07 11:25:20 :Product1
Customer1:2011/01/07 11:25:20 :Product2
Customer1:2011/01/07 11:25:20 :Product3
همانطور که مشاهده میکنید در اینجا اطلاعات از 4 جدول مختلف دریافت میشوند اما ما Join ایی را ننوشتهایم. ORM هرجایی که به اطلاعات فیلدهای جداول دیگر نیاز داشته، به صورت مستقیم به آن جدول مراجعه کرده و یک کوئری، حاصل این عملیات خواهد بود (مطابق تصویر جمعا 6 کوئری در پشت صحنه برای نمایش سه سطر خروجی فوق اجرا شده است).
این حالت فقط و فقط با تعداد رکورد کم بهینه است (و به همین دلیل هم تدارک دیده شده است). بنابراین اگر برای مثال قصد نمایش اطلاعات حاصل از 4 جدول فوق را در یک گرید داشته باشیم، بسته به تعداد رکوردها و تعداد کاربران همزمان برنامه (خصوصا در برنامههای تحت وب)، بانک اطلاعاتی باید بتواند هزاران هزار کوئری رسیده حاصل از lazy loading را پردازش کند و این یعنی مصرف بیش از حد منابع (IO بالا، مصرف حافظه بالا) به همراه بالا رفتن CPU usage و از کار افتادن زود هنگام سیستم.
کسانی که پیش از این با SQL نویسی خو گرفتهاند احتمالا الان منابع موجود را در مورد نحوهی نوشتن Join در NHibernate زیر و رو خواهند کرد؛ زیرا پیش از این آموختهاند که برای دریافت اطلاعات از دو یا چند جدول مرتبط باید Join نوشت. اما همانطور که پیشتر نیز عنوان شد، اگر با جزئیات کار با NHibernate آشنا شویم، نیازی به Join نویسی نخواهیم داشت. اینکار را خود ORM در پشت صحنه باید و میتواند مدیریت کند. اما چگونه؟
در NHibernate 3.0 با معرفی QueryOver که جایگزینی از نوع strongly typed همان ICriteria API قدیمی است، یا با معرفی Query که همان LINQ to NHibernate میباشد، متدی به نام Fetch نیز تدارک دیده شده است که استراتژیهای lazy loading و eager loading را به سادگی توسط آن میتوان مشخص نمود.
مثال: دریافت اطلاعات با استفاده از QueryOver
var list = session
.QueryOver<Customer>()
.Fetch(c => c.Orders).Eager
.Fetch(c => c.Orders.First().OrderItems).Eager
.Fetch(c => c.Orders.First().OrderItems.First().Product).Eager
.List();
foreach (var customer in list)
{
foreach (var order in customer.Orders)
{
foreach (var orderItem in order.OrderItems)
{
Console.WriteLine("{0}:{1}:{2}", customer.Name, order.OrderDate, orderItem.Product.Name);
}
}
}
پشت صحنه:
اینبار فقط یک کوئری حاصل عملیات بوده و join ها به صورت خودکار با توجه به متدهای Fetch ذکر شده که حالت eager loading آنها صریحا مشخص شده است، تشکیل شدهاند (6 بار رفت و برگشت به بانک اطلاعاتی به یکبار تقلیل یافت).
نکته 1: نتایج تکراری
اگر حاصل join آخر را نمایش دهیم، نتایجی تکراری خواهیم داشت که مربوط است به مقدار دهی customer با سه وهله از شیء مربوطه تا بتواند واکشی حریصانهی مجموعه اشیاء فرزند آنرا نیز پوشش دهد. برای رفع این مشکل یک سطر TransformUsing باید اضافه شود:
...
.TransformUsing(NHibernate.Transform.Transformers.DistinctRootEntity)
.List();
دریافت اطلاعات با استفاده از LINQ to NHibernate3.0
برای اینکه بتوان متدهای Fetch ذکر شده را به LINQ to NHibernate 3.0 اعمال نمود، ذکر فضای نام NHibernate.Linq ضروری است. پس از آن خواهیم داشت:
var list = session
.Query()
.FetchMany(c => c.Orders)
.ThenFetchMany(o => o.OrderItems)
.ThenFetch(p => p.Product)
.ToList();
اینبار از FetchMany، سپس ThenFetchMany (برای واکشی حریصانه مجموعههای فرزند) و در آخر از ThenFetch استفاده خواهد شد.
همانطور که ملاحظه میکنید حاصل این کوئری، با کوئری قبلی ذکر شده یکسان است. هر دو، اطلاعات مورد نیاز از دو جدول مختلف را نمایش میدهند. اما یکی در پشت صحنه شامل چندین و چند کوئری برای دریافت اطلاعات است، اما دیگری تنها از یک کوئری Join دار تشکیل شده است.
نکته 2: خطاهای ممکن
ممکن است حین تعریف متدهای Fetch در زمان اجرا به خطاهای Antlr.Runtime.MismatchedTreeNodeException و یا Specified method is not supported و یا موارد مشابهی برخورد نمائید. تنها کاری که باید انجام داد جابجا کردن مکان بکارگیری extension methods است. برای مثال متد Fetch باید پس از Where در حالت استفاده از LINQ ذکر شود و نه قبل از آن.
پیاده سازی DbContext مورد نیاز
برای ساخت DbContext میتوان به صورت زیر عمل نمود:
namespace FsWeb.Repositories open System.Data.Entity open FsWeb.Models type FsMvcAppEntities() = inherit DbContext("FsMvcAppExample") do Database.SetInitializer(new CreateDatabaseIfNotExists<FsMvcAppEntities>()) [<DefaultValue()>] val mutable books: IDbSet<Guitar> member x.Books with get() = x.books and set v = x.books <- v
اگر syntax زبان #F برایتان نامفهوم است میتوانید از این دوره کمک بگیرید.
پیاده سازی کلاس BookRepository
ابتدا به کدهای زیر دقت کنید:
namespace FsWeb.Repositories type BooksRepository() = member x.GetAll () = use context = new FsMvcAppEntities() query { for g in context.Books do select g } |> Seq.toList
در بخش بعدی بک کوئری از DbSet مورد نظر گرفته میشود. این روش Query گرفتن در F# 3.0 مطرح شده است. در نتیجه در نسخههای قبلی آن (F# 2.0) اجرای این کوئری باعث خطا میشود. اگر قصد دارید با استفاده از F# 2.0 کوئریهای خود را ایجاد نماید باید به طریق زیر عمل نمایید:
ابتدا از طریق nuget اقدام به نصب package ذیل نمایید:
FSPowerPack.Linq.Community
query <@ seq { for g in context.Books -> g } @> |> Seq.toList
[<HandleError>] type BooksController(repository : BooksRepository) = inherit Controller() new() = new BooksController(BooksRepository()) member this.Index () = repository.GetAll() |> this.View
نکته : تنظیمات مروط به ConnectionString را فراموش نکنید:
<add name="FsMvcAppExample" connectionString="YOUR CONNECTION STRING" providerName="System.Data.SqlClient" />
گاهی اوقات انتخاب عناصر با توجه به مکان آنها و یا موقعیت مکانی آنها نسبت به سایر اجزا صورت میپذیرد؛ برای مثال اولین لینک صفحه و یا اولین لینک هر پاراگراف و یا گزینهی آخر از لیست، jQuery شیوه ای خاص را برای چنین انتخاب هایی ارایه کرده است. برای مثال دستور زیر اولین لینک موجود در صفحه را انتخاب میکند:
a:first
دستور زیر چکاری انجام میدهد؟
p:odd
دستور بالا تمامی پاراگرافهای فرد را انتخاب میکند. روشهای دیگری هم ممکن است بخواهیم استفاده کنیم؛ مثلا دستور زیر تمامی پاراگرافهای زوج را انتخاب میکند:
p:even
یا با استفاده از دستور زیر میتوان آخرین فرزند یک والد را انتخاب کرد؛ در زیر آخرین <lii> فرزند یک <ul> انتخاب میشود. علاوه بر انتخاب کننده هایی که ذکر شد؛ تعداد قابل توجه دیگری نیز وجود دارند که در جدول 2-2 ذکر شده اند.
توضیح | فیلتر |
اولین عنصر که با شرط ما مطابقت میکند را انتخاب میکند، li a:first اولین لینکی را که فرزند لیست به حساب میآیند؛ را بر میگرداند | :first |
آخرین عنصری که با شرط ما مطابقت کند را انتخاب میکند. li a:last آخرین لینک از فرزندان لیست را برمی گرداند. | :last |
اولین فرزند عنصر که با شرط ما مطابقت میکند را انتخاب میکند. li a:first-child اولین عنصر لینک از هر لیست را برمی گرداند. | :first-child |
آخرین فرزند عنصر که با شرط ما مطابقت میکند را انتخاب میکند. li a:last-child اولین عنصر لینک از هر لیست را برمی گرداند. | :last-child |
تمام عناصری که پدر انها تنها همان فرزند را داد، برمی گرداند. | :only-child |
nامین فرزند عنصری که با شرط ما مطابقت داشته باشد را انتخاب میکند. li: nth-child:(2) //comment دومین عنصر از هر لیست را برمی گرداند. | :nth-child(n) |
فرزندان زوج یا فرد عنصر را انخاب میکند. li:nth-child(even) //commentتمام عناصر زوج لیستها را بر میگرداند. | :nth-child(even یا odd) |
nامین فرزند عنصری که از طریق فرمول ارایه شده به دست میآید را انتخاب میکند. اگر Y صفر باشد، نیازی به نوشتن آن نیست. li:nth-child(3n)//comment تمام عناصر ضریب 3 لیستها را بر میگرداند، در حالی که li:nth-child(5n+1)// comment عناصری از لیست را برمیگرداند که بعد از عنصرهای ضریب 5 لیستها قرار گرفته باشند. | :nth-child(Xn+Y) |
تمام عناصر زوج یا فرد که با شرط ما مطابقت کنند را انتخاب میکند. li:evenتمامی عناصر زوج لیستها را بر میگرداند. | :even |
n امین عنصر انتخاب شده را برمی گرداند. | :eq(n) |
عناصر بعد از n امین عنصر را بر میگرداند. (در واقع عناصری که بزرگتر از عنصر n ام هستند را بر میگرداند) | :gt(n) |
عناصر قبل از n امین عنصر را بر میگرداند. (در واقع عناصری که کوچکتر از عنصر n ام هستند را بر میگرداند) | :lt(n) |
<table id="languages"> <thead> <tr> <th>Language</th> <th>Type</th> <th>Invented</th> </tr> </thead> <tbody> <tr> <td>Java</td> <td>Static</td> <td>1995</td> </tr> <tr> <td>Ruby</td> <td>Dynamic</td> <td>1993</td> </tr> <tr> <td>Smalltalk</td> <td>Dynamic</td> <td>1972</td> </tr> <tr> <td>C++</td> <td>Static</td> <td>1983</td> </tr> </tbody> </table>
table#languages tbody td:first-child
و یا با استفاده از دستور زیر این کار را انجام دهیم:
table#languages tbody td:nth-child(1)
اما دستور اول مختصرتر و خواناتر است. پس از آن برای دسترسی به نوع هریک از زبانهای برنامه نویسی، دستور انتخاب کننده دوم را به صورت
:nth-child(2)
:nth-child(3) یا :last-child
در پست بعدی انتخاب گرهای CSS و فیلترهای سفارشی jQuery را بررسی خواهیم کرد.
- از تاریخ سرور برای نمایش روز جاری استفاده میکند.
- به زبان جاوااسکریپت است و نیازی به سایر فریمورکها مانند jQuery ندارد.
- سبک و کم حجم است (3.5 کیلوبایت)
- روزها به صورت عمودی لیست شدهاند مانند برنامه هفتگی مدرسه که کاربران فارسیزبان با آن راحتترند.
طرز استفاده :
PersianDatePicker.Show(textBox, today)
today : تاریخ روز جاری است.
برای مثال :
<html> <head> <script type="text/javascript" src="Scripts/PersianDatePicker.min.js"></script> <link type="text/css" rel="stylesheet" href="Content/PersianDatePicker.min.css" /> </head> <body> <input type="text" onclick="PersianDatePicker.Show(this, '1392/03/22');" /> </body> </html>
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="WebApplication4.WebForm1" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> <script src="Scripts/PersianDatePicker.min.js"></script> <link href="Content/PersianDatePicker.min.css" rel="stylesheet" /> </head> <body> <form id="form1" runat="server"> <div> <asp:Label runat="server" Text="Label" AssociatedControlID="TextBox1">تاریخ : </asp:Label> <asp:TextBox runat="server" ID="TextBox1"></asp:TextBox> </div> </form> </body> </html>
public partial class WebForm1 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { var now = PersianDateTime.Now; var today = now.ToString(PersianDateTimeFormat.Date); TextBox1.Attributes["onclick"] = "PersianDatePicker.Show(this,'" + today + "');"; } }
در مثال بالا برای گرفتن تاریخ روز جاری از PersianDateTime استفاده شده است.
در ASP.NET MVC یا PHP یا هر زبان دیگری کافیست onclick کنترل متنی مورد نظر در سمت سرور مانند مثال بالا مقدار داده شود. یا میتوان image کوچکی کنار textbox مورد نظر قرار داد و onclick آن تنظیم شود.
برای استفاده از PersianDatePicker میتوانید آنرا از NuGet دریافت کنید :
Senior Developer به چه کسی گفته می شود؟
بازهها و الگوهای بازگشتی در C# 8
- C# 8 Adds Ranges and Recursive Patterns
- Ranges easily define a sequence of data, replacing the Enumberable.Range()
- Recursive Patterns brings an F#-like construct to C#
- Recursive Patterns is an awesome feature, it giving you the flexibility to testing the data against a sequence of conditions and performing further computations based on the condition met.
- Ranges is very useful to generate sequences of numbers in the form of a collection or a list.
Roslyn #1
PM> Uninstall-package Microsoft.CodeDom.Providers.DotNetCompilerPlatform PM> Uninstall-package Microsoft.Net.Compilers
<Target Name="CopyRoslynFiles" AfterTargets="AfterBuild" Condition="!$(Disable_CopyWebApplication) And '$(OutDir)' != '$(OutputPath)'"> <ItemGroup> <RoslynFiles Include="$(CscToolPath)\*" /> </ItemGroup> <MakeDir Directories="$(WebProjectOutputDir)\bin\roslyn" /> <Copy SourceFiles="@(RoslynFiles)" DestinationFolder="$(WebProjectOutputDir)\bin\roslyn" SkipUnchangedFiles="true" Retries="$(CopyRetryCount)" RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)" /> </Target>
<system.codedom> <compilers> <compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:6 /nowarn:1659;1699;1701"></compiler> <compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:14 /nowarn:41008 /define:_MYTYPE=\"Web\" /optionInfer+"></compiler> </compilers> </system>
Senior Developer .Net Technologies Resume : ------------------------------------
احتمالا اکثر شما هم در آگهیهای استخدام برنامه نویس، با واژه Senior Developer یا برنامه نویس ارشد برخورد داشته اید. حال به راستی به چه کسی برنامه نویس ارشد گفته میشود. یا بهتره بگم که چه زمانی ما میتونیم خودمون را یک برنامه نویس ارشد بدونیم؟
از آنجا که تعریف مشخصی برای این واژهها در فرهنگ خاصی وجود ندارد و عموما هر کسی یک تعریف خاص با توجه به سلیقه خودش در این زمینه ارائه میدهد نمیتوان یک شرح واحد از این واژهها نظیر Senior Developer یا حتی Junior Developer ارائه داد. در نتیجه قصد دارم با توجه به تجربیات شخصی، این موارد رو تشریح کنم.
در خیلی موارد منظور از برنامه نویس ارشد، کسی است که:
- در حداقل یک یا دو زبان برنامه نویسی تجربه داشته باشد؛
- با تکنولوژیهای مهم و کارآمد ارائه شده در آن زبان آشنایی کامل داشته باشد؛
- توانایی استفاده از این تکنولوژیها را در جای مناسب پروژه دارا باشد؛
- با الگوهای برنامه نویسی (Design Pattern) آشنایی کامل داشته باشد؛
- بتواند به سایر اعضای تیم در حل مشکلات و باگهای برنامه کمک کند؛
- یک خطایاب قهار باشد(منظور این است با اکثر استثنائات و خطاهای متداول زبان و تکنولوژی که پروژه با آن پیاده سازی میشود آشنا باشد و بداند که چه زمان این خطا به وجود میآید و چگونه میتوان این موارد را برطرف کرد)؛
- باید با پیاده سازی سیستمهای سرویس گرا (SOA) جدای از تکنولوژی پیاده سازی آن آشنایی کامل داشته باشد؛
- باید با روشهای تست و خطایابی و ابزارهای توسعه و پیاده سازی آن آشنا بوده و توانایی استفاده از آنها را در پروژههای خود داشته باشد؛
- یک برنامه نویس ارشد باید در زمینه تخمین زمان تکمیل پروژه مهارت داشته باشد. باید بتواند مدت زمان لازم برای تکمیل یک Task را تخمین بزند و در همین مدت زمان، Task مربوطه را به صورت کامل پیاده سازی نماید.
- در خیلی موارد باید بتواند هماهنگی لازم را بین اعضای تیم برنامه نویسی ایجاد نماید؛
- و...
*ترتیب عبارات بالا دلیلی بر اولویت موارد ذکر شده نیست.
*منظور از آشنایی در عبارات بالا، یعنی تسلط و توانایی استفاده از آن با آگاهی تمام.
چه مدت زمان برای تبدیل شدن به یک برنامه نویس ارشد نیاز است؟
برای به دست آوردن مهارتها و تواناییهای یک برنامه نویس ارشد لازم است که حداقل 8 تا 10 سال تجربه برنامه نویسی در یک زبان را داشته باشید. البته این در مورد همگان صادق نیست . شاید کسی بتواند این راه را در کمتر از 8 سال طی نماید. این بستگی به تلاش و استعداد فرد دارد. اما تجربه بیشتر یعنی شرکت در پروژههای بیشتر و آمادگی بیشتر در حل مشکلات و مسائل مختلف در طی روند تکمیل پروژه. برای مثال در زمینه تخمین اجرای یک Task داشتن تجربه بیشتر خیلی به شما کمک خواهد کرد.
در پایان چهار ردیف یا رده مختلف را که بین برنامه نویسان رواج دارد ذکر میکنم.(از پایین به بالا)
Junior : عموما به کسی گفته میشود که 1 تا 3 سال تجربه برنامه نویسی دارد. معمولا کدهای نوشته شده توسط این افراد باید بررسی شود چون احتمال اشتباه در آن زیاد است. اکثر کدهای نوشته شده توسط این افراد به صورت dirty code است. راهنمایی هایی که به این افراد داده میشود شامل راهنمایی در زمینه ساختاری و الگوریتمی نیز میباشد.
Mid-Level : برنامه نویسان در این رده بین 4 تا 6 سال تجربه دارند. میتوانند به تنهایی نیز یک مشکل موجود در پروژه را حل نمایند. با مباحث مربوطه به طراحی کامپوننت آشنایی دارند و پروژه را بی نیاز از کامپوننت خواهند کرد. حتی در بعضی موارد میتوانند به تنهایی یک پروژه در سطح کوچک با متوسط را توسعه دهند.
Senior Developer : در بالا به صورت کامل شرح داده شد.
Luminary : به صورت معمول به کسی گفته میشود که تمام تواناییهای یک برنامه نویس ارشد را داراست و فقط تجربه برنامه نویسی آن قطعا از 10 سال بیشتر است.
از این واژه کمتر در ردههای برنامه نویسی استفاده میشود و بیشتر به همان واژه Senior Developer بسنده میکنند.