چه تکنولوژی می‌خواهید یاد بگیرید؟

نظرات و انتقادات خود را با ما در میان بگذارید

آپدیت روزانه

بیش از 1500 مطلب آموزشی

نیازهای روز

مفهوم کلاس‌های انتزاعی(abstract classes) در زبان سی‌شارپ

کلاس‌های انتزاعی(به انگلیسی Abstract Classes) یکی از رفتارهای ارائه شده در برنامه‎نویسی شی‌ءگرا است که معمولا به منظور کلاس پایه و مرجع به صورت کلمه کلیدی abstract و با حروف کوچک قبل از class تعریف می‌گردد. کلاس ایجاد شده از نوع abstract بدین معنی است که شما قادر به ایجاد نمونه سازی از آن نخواهید بود اما امکان مشتقات برای آن فراهم است همچنین هر کلاس فقط قادر خواهد بود از یک abstract کلاس ارث بری نماید. روش‌های پیاده سازی Polymorphism استفاده از رفتارهای abstract در کلاس والد است، توضیح کوتاه Polymorphism، قابلیت تعریف مجدد رفتار یک موجودیت در کلاس‌های فرزند می‌باشد.

بیان ساده‌، هر کلاس که یک رفتار غیر قطعی داشته باشد آن کلاس تبدیل به یک کلاس abstract خواهد شد. سناریو در نظر داشته باشید که در آن یک پروژه وب(ASP.Net Web Forms) به همراه وب‌سرویس(WCF) در حال توسعه است، قابلیت جدید اضافه و نیاز به پیاده سازی خواهد بود برای مثال ثبت گزارش درخواست‌های ارسالی به سمت سرور. رفتارهای پیاده سازی ثبت گزارش درخواست‌ها در هر دو پروژه کاملا متفاوت است اما هر دو پروژه یک قابلیت را پیاده خواهند کرد و آن هم ثبت درخواست‌های ارسالی به سمت سرور است، در صورت که دید و مهارت شی‌گرایی را به درستی درک کرده باشید یقینا برای پیاده سازی این ماژول از کلاس پایه abstract استفاده خواهید کرد:

namespace Abstract.Models
{
    abstract class BaseClass
    {
    }
}

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

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

namespace Abstract.Models
{
    abstract class BaseClass
    {
        public abstract void AbstractMethod();
    }
}

نکته‌ای که در خصوص اعضا عضو کلاس‌های abstract نیاز است همچنین به خاطر بسپارید قابلیت تعریف کلاس abstract بدون داشتن حتی یک عضو abstract می‌باشد:

namespace Abstract.Models
{
    abstract class BaseClass
    {
        public void NonAbstractMethod()
        {
            System.Console.WriteLine("NonAbstractMethod");
        }
    }
}

قوانین مهم اعمال شده بر روی کلاس‌های Abstract:

کلاس‌های abstract نمی‌توانند کلاس‌های از نوع sealed باشند:

namespace Abstract.Models
{
    // Incorrect
    public abstract sealed class BaseClass
    {
    }
}

متدهای abstract فقط قابل تعریف در کلاس‌های abstract هستند همچنین این متدها سطح دسترسی private را در بر نخواهند داشت و بدنه آنها در کلاس مشتق شده الزاما نیاز به پیاده سازی دارند.

namespace Abstract.Models
{
    public abstract class BaseClass
    {
        // Incorrect
        private abstract void AbstractMethod();
    }
}

سطح دسترسی متدهای بازنویسی(به انگلیسی override) شده در کلاس‌های مشتق الزامی است همانند سطح دسترسی متد abstract موجود در کلاس پایه تعریف گردد در غیر این صورت با خطا کامپایلری مواجه خواهید شد:

-- Example
Cannot change access modifiers when overriding 'protected' inherited member

متدهای abstract نمی‌توانند با کلمه کلیدی virtual مهر شوند چرا که خود متدهای abstract به طور ضمنی آن را پیاده می‌کند بنابراین برای پیاده سازی این متدها در کلاس های مشتق نیاز به استفاده از کلمه کلیدی override می باشد:

namespace Abstract.Models
{
    public abstract class BaseClass
    {
        // Incorrect
        protected virtual abstract void AbstractMethod();
    }
}

متدهای abstract نمی‌توانند static تعریف شوند:

namespace Abstract.Models
{
    public abstract class BaseClass
    {
        // Incorrect
        protected abstract static void AbstractMethod();
    }
}

مثال:

به منظور درک بهتر کلاس و متد abstract، مثال کاربری تهیه گردید که قادر بکار گیری در پروژه‌های خود خواهید بود:

public enum RequestCategory
{
    Site = 1,
    Api = 2
}

RequestCategory در واقع دسته‌ بندی است که دو حالت مختلف به منظور پیاده سازی رفتار جدید بکار رفته است که وب‌سایت با یک وب‌سرویس را شامل می‌شود:

private abstract class RequestLoggerBase
{
    protected abstract string GetUsername();

    protected abstract string GetUrl();

    protected abstract string GetReferrerUrl();

    protected abstract string GetClientIpAddress();

    protected abstract bool IsValid();

    public abstract RequestCategory RequestCategory { get; }

    public void Log()
    {
        try
        {
            if (IsValid())
            {
                var ipaddress = GetClientIpAddress();
                var username = GetUsername();
                var url = GetUrl();
                var urlReferrer = GetReferrerUrl();
                new RequestFacade().LogRequest(ipaddress, DateTime.UtcNow, RequestCategory, username, url, urlReferrer);
            }
        }
        catch (Exception e)
        {
            LoggingProvider.Logger.Error(e);
        }
    }
}

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

private class HttpRequestLogger : RequestLoggerBase
{
    public override RequestCategory RequestCategory
    {
        get
        {
            return RequestCategory.Site;
        }
    }

    protected override string GetClientIpAddress()
    {
        HttpRequest req = HttpContext.Current.Request;
        var ipAddress = req.ServerVariables["HTTP_X_FORWARDED_FOR"];
        if (string.IsNullOrEmpty(ipAddress))
            ipAddress = req.ServerVariables["REMOTE_ADDR"];

        return ipAddress;
    }

    protected override string GetReferrerUrl()
    {
        if (HttpContext.Current != null)
            return HttpContext.Current.Request.UrlReferrer == null ? string.Empty : HttpContext.Current.Request.UrlReferrer.ToString();

        return string.Empty;
    }

    protected override string GetUrl()
    {
        if (HttpContext.Current != null)
            return HttpContext.Current.Request.Url.ToString();

        return string.Empty;
    }

    protected override string GetUsername()
    {
        if (HttpContext.Current != null && HttpContext.Current.User != null && HttpContext.Current.User.Identity != null)
            return HttpContext.Current.User.Identity.Name;

        return string.Empty;
    }

    protected override bool IsValid()
    {
        if (HttpContext.Current == null)
            return false;

        if (HttpContext.Current.Request.Url.AbsolutePath.EndsWith("aspx") && HttpContext.Current.Request.HttpMethod == "POST")
        {
            return false;
        }

        var extensionStaticFile = new[] { ".js", ".css", ".png", ".jpeg", ".html", ".gif", ".jpg", ".woff", ".woff2", ".axd" };
        var url = HttpContext.Current.Request.Url.AbsolutePath;
        return !extensionStaticFile.Any(ext => url.EndsWith(ext, StringComparison.InvariantCultureIgnoreCase));
    }
}

private class WcfRequestLogger : RequestLoggerBase
{
    public override RequestCategory RequestCategory
    {
        get
        {
            return RequestCategory.Api;
        }
    }

    public WcfRequestLogger()
    {
    }

    protected override string GetClientIpAddress()
    {
        if (OperationContext.Current != null)
        {
            var context = OperationContext.Current;
            var messageProperties = context.IncomingMessageProperties;
            var endpointProperty = messageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
            return endpointProperty.Address;
        }

        return string.Empty;
    }

    protected override string GetReferrerUrl()
    {
        return null;
    }

    protected override string GetUrl()
    {
        if (OperationContext.Current != null)
        {
            var context = OperationContext.Current;

            var match = Regex.Match(context.IncomingMessageHeaders.Action, @"^(.)*/(?<ActionName>.*)$");
            if (match.Success && !string.IsNullOrWhiteSpace(match.Groups["ActionName"].Value))
                return string.Format("{0}/{1}", context.Channel.LocalAddress.Uri, match.Groups["ActionName"].Value);

            return context.Channel.LocalAddress.Uri.ToString();
        }

        return string.Empty;
    }

    protected override string GetUsername()
    {
        if (OperationContext.Current != null)
        {
            if (OperationContext.Current.IncomingMessageProperties.ContainsKey("UserName"))
                return OperationContext.Current.IncomingMessageProperties["UserName"].ToString();
        }

        return null;
    }

    protected override bool IsValid()
    {
        return (OperationContext.Current != null);
    }
}

HttpRequestLogger و WcfRequestLogger کلاس‌های فرزندی هستند که کلاس ابسترکت را ارث بری نموده و رفتارهای خاص خود را پیاده می‌کنند.

کلاس نهایی:

کلاس نهایی ثبت درخواست‌های ارسالی به سمت سرور در کلاس RequestLogger به صورت زیر تعریف گردید:

using BusinessLogicLayer.Components.BaseInfo;
using BusinessLogicLayer.Entities;
using Helper.Persian.PersianDateTime;
using OperationalManagement.Logging;
using System;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Text.RegularExpressions;
using System.Web;

namespace BusinessFacadeLayer.BaseInfo.User
{
    public class RequestLogger
    {
        public void LogRequest(RequestCategory requestCategory)
        {
            try
            {
                RequestLoggerBase logger = null;
                switch (requestCategory)
                {
                    case RequestCategory.Site:
                        logger = new HttpRequestLogger();
                        break;

                    case RequestCategory.Api:
                        logger = new WcfRequestLogger();
                        break;
                }

                logger?.Log();
            }
            catch(Exception ex)
            {
                LoggingProvider.Logger.Error(ex);
            }
        }

        private abstract class RequestLoggerBase
        {
            protected abstract string GetUsername();

            protected abstract string GetUrl();

            protected abstract string GetReferrerUrl();

            protected abstract string GetClientIpAddress();

            protected abstract bool IsValid();

            public abstract RequestCategory RequestCategory { get; }

            public void Log()
            {
                try
                {
                    if (IsValid())
                    {
                        var ipaddress = GetClientIpAddress();
                        var username = GetUsername();
                        var url = GetUrl();
                        var urlReferrer = GetReferrerUrl();
                        new RequestFacade().LogRequest(ipaddress, DateTime.UtcNow), RequestCategory, username, url, urlReferrer);
                    }
                }
                catch (Exception e)
                {
                    LoggingProvider.Logger.Error(e);
                }
            }
        }

        private class HttpRequestLogger : RequestLoggerBase
        {
            public override RequestCategory RequestCategory
            {
                get
                {
                    return RequestCategory.Site;
                }
            }

            protected override string GetClientIpAddress()
            {
                HttpRequest req = HttpContext.Current.Request;
                var ipAddress = req.ServerVariables["HTTP_X_FORWARDED_FOR"];
                if (string.IsNullOrEmpty(ipAddress))
                    ipAddress = req.ServerVariables["REMOTE_ADDR"];

                return ipAddress;
            }

            protected override string GetReferrerUrl()
            {
                if (HttpContext.Current != null)
                    return HttpContext.Current.Request.UrlReferrer == null ? string.Empty : HttpContext.Current.Request.UrlReferrer.ToString();

                return string.Empty;
            }

            protected override string GetUrl()
            {
                if (HttpContext.Current != null)
                    return HttpContext.Current.Request.Url.ToString();

                return string.Empty;
            }

            protected override string GetUsername()
            {
                if (HttpContext.Current != null && HttpContext.Current.User != null && HttpContext.Current.User.Identity != null)
                    return HttpContext.Current.User.Identity.Name;

                return string.Empty;
            }

            protected override bool IsValid()
            {
                if (HttpContext.Current == null)
                    return false;

                if(HttpContext.Current.Request.Url.AbsolutePath.EndsWith("aspx") && HttpContext.Current.Request.HttpMethod == "POST")
                {
                    return false;
                }

                var extensionStaticFile = new[] { ".js", ".css", ".png", ".jpeg", ".html", ".gif", ".jpg", ".woff", ".woff2", ".axd" };
                var url = HttpContext.Current.Request.Url.AbsolutePath;
                return !extensionStaticFile.Any(ext => url.EndsWith(ext, StringComparison.InvariantCultureIgnoreCase));
            }
        }

        private class WcfRequestLogger : RequestLoggerBase
        {
            public override RequestCategory RequestCategory
            {
                get
                {
                    return RequestCategory.Api;
                }
            }

            public WcfRequestLogger()
            {
            }

            protected override string GetClientIpAddress()
            {
                if (OperationContext.Current != null)
                {
                    var context = OperationContext.Current;
                    var messageProperties = context.IncomingMessageProperties;
                    var endpointProperty = messageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
                    return endpointProperty.Address;
                }

                return string.Empty;
            }

            protected override string GetReferrerUrl()
            {
                return null;
            }

            protected override string GetUrl()
            {
                if (OperationContext.Current != null)
                {
                    var context = OperationContext.Current;

                    var match = Regex.Match(context.IncomingMessageHeaders.Action, @"^(.)*/(?<ActionName>.*)$");
                    if (match.Success && !string.IsNullOrWhiteSpace(match.Groups["ActionName"].Value))
                        return string.Format("{0}/{1}", context.Channel.LocalAddress.Uri, match.Groups["ActionName"].Value);

                    return context.Channel.LocalAddress.Uri.ToString();
                }

                return string.Empty;
            }

            protected override string GetUsername()
            {
                if (OperationContext.Current != null)
                {
                    if (OperationContext.Current.IncomingMessageProperties.ContainsKey("UserName"))
                        return OperationContext.Current.IncomingMessageProperties["UserName"].ToString();
                }

                return null;
            }

            protected override bool IsValid()
            {
                return (OperationContext.Current != null);
            }
        }
    }
}

امید نصری

0 نظر:

تعداد دیدگاه‌های کاربران : 0 دیدگاه
مهمان گرامی! برای ارسال نظر نیاز است وارد سایت شوید.


You must log on to comment.