لماذا/متى يجب استخدام الطبقات المتداخلة في .الشبكة ؟ أو لا يجب ؟

StackOverflow https://stackoverflow.com/questions/48872

  •  09-06-2019
  •  | 
  •  

سؤال

في كاثلين Dollard مؤخرا بلوق وظيفة, انها مثيرة لاهتمام سبب لاستخدام الطبقات المتداخلة في .صافي.إلا أنها تشير أيضا إلى أن التقدم لا يحب الطبقات المتداخلة.أنا على افتراض أن الناس في الكتابة على التقدم القواعد ليست غبية ، لذلك يجب أن يكون هناك منطق وراء هذا الموقف, لكني لم أكن قادرة على العثور عليه.

هل كانت مفيدة؟

المحلول

استخدام فئة متداخلة عند الطبقة كنت التعشيش هو مفيد فقط إلى أرفق الدرجة.فعلى سبيل المثال ، الطبقات المتداخلة تسمح لك أن تكتب شيئا مثل (المبسطة):

public class SortedMap {
    private class TreeNode {
        TreeNode left;
        TreeNode right;
    }
}

يمكنك جعل إكمال تعريف الفئة الخاصة بك في مكان واحد, لم يكن لديك للقفز من خلال أي PIMPL الأطواق لتحديد مدى صفك يعمل و العالم الخارجي لا تحتاج إلى أي تنفيذ.

إذا كان TreeNode الطبقة الخارجية ، إما أن تجعل جميع المجالات public أو جعل حفنة من get/set طرق استخدامه.العالم الخارجي أن يكون آخر الطبقة الملوثة بهم التحسس الذكي.

نصائح أخرى

من الشمس جافا البرنامج التعليمي:

لماذا استخدام متداخلة الطبقات ؟ هناك عدة أسباب مقنعة باستخدام الطبقات المتداخلة ، من بينها:

  • بل هو وسيلة منطقيا تجميع الصفوف التي تستخدم فقط في مكان واحد.
  • لأنه يزيد التغليف.
  • الطبقات المتداخلة يمكن أن يؤدي إلى المزيد للقراءة كود للصيانة.

تجميع منطقي من الطبقات—إذا كانت الدرجة مفيدة واحدة فقط فئة أخرى ، ثم فمن المنطقي أن تضمين ذلك في الصف والحفاظ على الاثنين معا.التعشيش مثل "مساعد الفئات" يجعل حزمة أكثر تبسيطا.

زيادة التغليف—النظر في اثنين من أعلى مستوى الطبقات A و B حيث B يحتاج إلى الوصول إلى أعضاء لولا ذلك أعلن خاصة.عن طريق إخفاء فئة ب في الدرجة الأولى ، أعضاء يمكن تعريف خاص و ب يمكن الوصول إليها.وبالإضافة إلى ذلك ، ب نفسها يمكن أن تكون مخفية عن العالم الخارجي. <- هذا لا ينطبق C#'s تنفيذ الطبقات المتداخلة ، هذا ينطبق فقط على جافا.

أكثر قابلية للقراءة, كود للصيانة—تداخل فصول صغيرة في أعلى مستوى الطبقات الأماكن رمز أقرب إلى حيث يتم استخدامه.

تماما كسول مؤشر الترابط-الآمن نمط المفرد

public sealed class Singleton
{
    Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            return Nested.instance;
        }
    }

    class Nested
    {
        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static Nested()
        {
        }

        internal static readonly Singleton instance = new Singleton();
    }
}

المصدر: http://www.yoda.arachsys.com/csharp/singleton.html

ذلك يعتمد على الاستخدام.أنا نادرا ما من أي وقت مضى استخدام العامة المتداخلة الدرجة ولكن استخدام خاصة الطبقات المتداخلة كل الوقت.خاصة متداخلة فئة يمكن أن تستخدم الفرعي الكائن الذي يتم استخدامه فقط داخل الأم.مثال على ذلك سيكون إذا HashTable فئة يحتوي على إدخال القطاع الخاص الكائن لتخزين البيانات داخليا فقط.

إذا كان الفصل هو من المفترض أن يتم استخدامها من قبل المتصل (خارجيا) ، عموما أنا مثل يجعلها منفصلة بذاتها الدرجة.

بالإضافة إلى الأسباب الأخرى المذكورة أعلاه, هناك سبب واحد أكثر من أن أستطيع التفكير ليس فقط إلى استخدام الطبقات المتداخلة ، ولكن في الواقع العام الطبقات المتداخلة.بالنسبة لأولئك الذين يعملون مع عامة متعددة الطبقات التي تشترك في نفس نوع عام المعلمات القدرة على إعلان عام مساحة من شأنه أن يكون مفيدا للغاية.للأسف .صافي (أو على الأقل C#) لا يدعم فكرة عامة مساحات الأسماء.وذلك من أجل تحقيق نفس الهدف ، يمكننا استخدام عامة الطبقات لتحقيق نفس الهدف.خذ المثال التالي فئات ذات صلة منطقية الكيان:

public  class       BaseDataObject
                    <
                        tDataObject, 
                        tDataObjectList, 
                        tBusiness, 
                        tDataAccess
                    >
        where       tDataObject     : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
        where       tBusiness       : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataAccess     : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}

public  class       BaseDataObjectList
                    <
                        tDataObject, 
                        tDataObjectList, 
                        tBusiness, 
                        tDataAccess
                    >
:   
                    CollectionBase<tDataObject>
        where       tDataObject     : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
        where       tBusiness       : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataAccess     : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}

public  interface   IBaseBusiness
                    <
                        tDataObject, 
                        tDataObjectList, 
                        tBusiness, 
                        tDataAccess
                    >
        where       tDataObject     : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
        where       tBusiness       : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataAccess     : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}

public  interface   IBaseDataAccess
                    <
                        tDataObject, 
                        tDataObjectList, 
                        tBusiness, 
                        tDataAccess
                    >
        where       tDataObject     : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
        where       tBusiness       : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataAccess     : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}

يمكننا تبسيط التوقيعات من هذه الفئات باستخدام عامة مساحة (تنفذ عبر الطبقات المتداخلة):

public
partial class   Entity
                <
                    tDataObject, 
                    tDataObjectList, 
                    tBusiness, 
                    tDataAccess
                >
        where   tDataObject     : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObject
        where   tDataObjectList : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObjectList, new()
        where   tBusiness       : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseBusiness
        where   tDataAccess     : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseDataAccess
{

    public  class       BaseDataObject {}

    public  class       BaseDataObjectList : CollectionBase<tDataObject> {}

    public  interface   IBaseBusiness {}

    public  interface   IBaseDataAccess {}

}

ثم من خلال استخدام فئات جزئية كما اقترح إيريك فان Brakel في تعليق سابق ، يمكنك فصل من فصول منفصلة في الملفات متداخلة.أوصي باستخدام Visual Studio امتداد مثل NestIn لدعم تداخل جزئي فئة ملفات.يسمح هذا "الاسم" الطبقة الملفات أيضا تستخدم لتنظيم متداخلة فئة الملفات في مجلد مثل الطريقة.

على سبيل المثال:

الكيان.cs

public
partial class   Entity
                <
                    tDataObject, 
                    tDataObjectList, 
                    tBusiness, 
                    tDataAccess
                >
        where   tDataObject     : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObject
        where   tDataObjectList : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObjectList, new()
        where   tBusiness       : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseBusiness
        where   tDataAccess     : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseDataAccess
{
}

الكيان.BaseDataObject.cs

partial class   Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{

    public  class   BaseDataObject
    {

        public  DataTimeOffset  CreatedDateTime     { get; set; }
        public  Guid            CreatedById         { get; set; }
        public  Guid            Id                  { get; set; }
        public  DataTimeOffset  LastUpdateDateTime  { get; set; }
        public  Guid            LastUpdatedById     { get; set; }

        public
        static
        implicit    operator    tDataObjectList(DataObject dataObject)
        {
            var returnList  = new tDataObjectList();
            returnList.Add((tDataObject) this);
            return returnList;
        }

    }

}

الكيان.BaseDataObjectList.cs

partial class   Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{

    public  class   BaseDataObjectList : CollectionBase<tDataObject>
    {

        public  tDataObjectList ShallowClone() 
        {
            var returnList  = new tDataObjectList();
            returnList.AddRange(this);
            return returnList;
        }

    }

}

الكيان.IBaseBusiness.cs

partial class   Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{

    public  interface   IBaseBusiness
    {
        tDataObjectList Load();
        void            Delete();
        void            Save(tDataObjectList data);
    }

}

الكيان.IBaseDataAccess.cs

partial class   Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{

    public  interface   IBaseDataAccess
    {
        tDataObjectList Load();
        void            Delete();
        void            Save(tDataObjectList data);
    }

}

الملفات في visual studio "مستكشف الحلول" ثم سيتم تنظيمها على هذا النحو:

Entity.cs
+   Entity.BaseDataObject.cs
+   Entity.BaseDataObjectList.cs
+   Entity.IBaseBusiness.cs
+   Entity.IBaseDataAccess.cs

وسوف تنفذ مساحة عامة كما يلي:

المستخدم.cs

public
partial class   User
:
                Entity
                <
                    User.DataObject, 
                    User.DataObjectList, 
                    User.IBusiness, 
                    User.IDataAccess
                >
{
}

المستخدم.DataObject.cs

partial class   User
{

    public  class   DataObject : BaseDataObject 
    {
        public  string  UserName            { get; set; }
        public  byte[]  PasswordHash        { get; set; }
        public  bool    AccountIsEnabled    { get; set; }
    }

}

المستخدم.DataObjectList.cs

partial class   User
{

    public  class   DataObjectList : BaseDataObjectList {}

}

المستخدم.IBusiness.cs

partial class   User
{

    public  interface   IBusiness : IBaseBusiness {}

}

المستخدم.IDataAccess.cs

partial class   User
{

    public  interface   IDataAccess : IBaseDataAccess {}

}

والملفات التي سيتم تنظيمها في "مستكشف الحلول" على النحو التالي:

User.cs
+   User.DataObject.cs
+   User.DataObjectList.cs
+   User.IBusiness.cs
+   User.IDataAccess.cs

ما سبق هو مثال بسيط باستخدام الخارجي فئة عامة مساحة الاسم.لقد بنيت "عامة مساحات الأسماء" التي تحتوي 9 أو أكثر من نوع المعلمات في الماضي.الحاجة إلى الحفاظ على تلك معلمات نوع متزامنة عبر تسعة أنواع جميعا في حاجة إلى معرفة نوع المعلمات كانت شاقة ، وخاصة عند إضافة معلمة جديدة.استخدام مساحات عامة يجعل هذا الرمز الآن أكثر سهولة للقراءة.

إذا فهمت Katheleen المادة على حق, انها تعتزم استخدام متداخلة الفصل أن تكون قادرا على الكتابة SomeEntity.جمع بدلا من EntityCollection< SomeEntity>.في رأيي انها مثيرة للجدل طريقة لتوفير بعض الكتابة.أنا متأكد من أن في العالم الحقيقي تطبيق مجموعات سوف يكون هناك بعض الاختلاف في التنفيذ ، لذا سوف تحتاج إلى إنشاء فئة منفصلة على أي حال.أعتقد أن استخدام اسم الفئة للحد من فئة أخرى نطاق ليست فكرة جيدة.فإنه يلوث التحسس وتعزيز تبعيات بين الطبقات.باستخدام مساحات الأسماء هو المعيار طريقة التحكم في الطبقات نطاق.ومع ذلك أجد أن استخدام الطبقات المتداخلة مثل @hazzen التعليق غير مقبول إلا إذا كان لديك طن من الطبقات المتداخلة التي هي علامة سيئة التصميم.

استخدام آخر بعد ذكر المتداخلة فصول هي الفصل بين أنواع عامة.على سبيل المثال, لنفترض أن أحد يريد أن يكون بعض العامة الأسر الساكنة الطبقات التي يمكن أن تتخذ الأساليب مع أعداد مختلفة من المعلمات ، جنبا إلى جنب مع قيم بعض من تلك المعايير ، وتوليد المندوبين مع عدد أقل من المعلمات.على سبيل المثال ، أحد يرغب في أن يكون أسلوب ثابت والتي يمكن أن تأخذ Action<string, int, double> وابن String<string, int> والتي سوف نداء الموردة العمل يمر 3.5 كما double;واحد قد ترغب في أن يكون أسلوب ثابت والتي يمكن أن تتخذ an Action<string, int, double> و تسفر عن Action<string>, مرورا 7 كما int و 5.3 كما double.باستخدام عامة الطبقات المتداخلة ، يمكن للمرء أن يرتب أن يكون أسلوب الدعاء يكون شيئا مثل:

MakeDelegate<string,int>.WithParams<double>(theDelegate, 3.5);
MakeDelegate<string>.WithParams<int,double>(theDelegate, 7, 5.3);

أو لأن هذا الأخير في كل أنواع التعبير يمكن أن يستدل على الرغم من تلك السابقة لا يمكن:

MakeDelegate<string,int>.WithParams(theDelegate, 3.5);
MakeDelegate<string>.WithParams(theDelegate, 7, 5.3);

باستخدام تداخل الأنواع العامة يجعل من الممكن أن أقول أي المندوبين تنطبق على أي أجزاء من العام نوع الوصف.

متداخلة الطبقات يمكن استخدامها الاحتياجات التالية:

  1. تصنيف البيانات
  2. عندما منطق الطبقة الرئيسية معقدة وكنت أشعر بأن كنت تحتاج إلى كائنات تابعة لإدارة الصف
  3. عند الدولة و وجود فئة يعتمد بشكل كامل على أرفق الدرجة

أنا غالبا ما تستخدم الطبقات المتداخلة لإخفاء تفاصيل التنفيذ. مثال من إريك ليبرت الجواب هنا:

abstract public class BankAccount
{
    private BankAccount() { }
    // Now no one else can extend BankAccount because a derived class
    // must be able to call a constructor, but all the constructors are
    // private!
    private sealed class ChequingAccount : BankAccount { ... }
    public static BankAccount MakeChequingAccount() { return new ChequingAccount(); }
    private sealed class SavingsAccount : BankAccount { ... }
}

هذا النمط يصبح أفضل حتى مع استخدام الأدوية. انظر هذا السؤال لمدة سنتين بارد الأمثلة.لذلك أنا في نهاية المطاف الكتابة

Equality<Person>.CreateComparer(p => p.Id);

بدلا من

new EqualityComparer<Person, int>(p => p.Id);

كما يمكن أن يكون قائمة عامة من Equality<Person> ولكن ليس EqualityComparer<Person, int>

var l = new List<Equality<Person>> 
        { 
         Equality<Person>.CreateComparer(p => p.Id),
         Equality<Person>.CreateComparer(p => p.Name) 
        }

حيث

var l = new List<EqualityComparer<Person, ??>>> 
        { 
         new EqualityComparer<Person, int>>(p => p.Id),
         new EqualityComparer<Person, string>>(p => p.Name) 
        }

ليس من الممكن.هذه فائدة متداخلة الدرجة وراثة من الفئة الأصل.

حالة أخرى (من نفس الطبيعة - إخفاء التنفيذ) هو عندما كنت تريد أن تجعل فئة أعضاء (الحقول خصائص الخ) يمكن الوصول إليها إلا عن فئة واحدة:

public class Outer 
{
   class Inner //private class
   {
       public int Field; //public field
   }

   static inner = new Inner { Field = -1 }; // Field is accessible here, but in no other class
}

كما نوفل ذكرت تنفيذ مصنع مجردة نمط هذا الرمز يمكن axtended لتحقيق فئة مجموعات نمط والتي تقوم على نمط مصنع مجردة.

أود أن عش الاستثناءات التي هي فريدة من نوعها إلى فئة واحدة ، أي.تلك التي لم القيت من أي مكان آخر.

على سبيل المثال:

public class MyClass
{
    void DoStuff()
    {
        if (!someArbitraryCondition)
        {
            // This is the only class from which OhNoException is thrown
            throw new OhNoException(
                "Oh no! Some arbitrary condition was not satisfied!");
        }
        // Do other stuff
    }

    public class OhNoException : Exception
    {
        // Constructors calling base()
    }
}

هذا يساعد على الحفاظ على ملفات المشروع الخاص بك مرتبة وليس كامل من مائة قصير قليلا استثناء الطبقات.

نضع في اعتبارنا أن كنت سوف تحتاج إلى اختبار فئة متداخلة.إذا كان القطاع الخاص ، أنت لن تكون قادرة على اختبار في عزلة.

هل يمكن أن تجعل من الداخلية ، على الرغم من ، بالتزامن مع InternalsVisibleTo السمة.ومع ذلك ، فإن هذا من شأنه أن يكون نفس جعل حقل خاص الداخلية فقط لأغراض الاختبار ، والتي أعتبرها سيئة الذاتي الوثائق.

لذا قد ترغب فقط في تنفيذ خاصة الطبقات المتداخلة التي تنطوي على تعقيد منخفضة.

نعم في هذه الحالة:

class Join_Operator
{

    class Departamento
    {
        public int idDepto { get; set; }
        public string nombreDepto { get; set; }
    }

    class Empleado
    {
        public int idDepto { get; set; }
        public string nombreEmpleado { get; set; }
    }

    public void JoinTables()
    {
        List<Departamento> departamentos = new List<Departamento>();
        departamentos.Add(new Departamento { idDepto = 1, nombreDepto = "Arquitectura" });
        departamentos.Add(new Departamento { idDepto = 2, nombreDepto = "Programación" });

        List<Empleado> empleados = new List<Empleado>();
        empleados.Add(new Empleado { idDepto = 1, nombreEmpleado = "John Doe." });
        empleados.Add(new Empleado { idDepto = 2, nombreEmpleado = "Jim Bell" });

        var joinList = (from e in empleados
                        join d in departamentos on
                        e.idDepto equals d.idDepto
                        select new
                        {
                            nombreEmpleado = e.nombreEmpleado,
                            nombreDepto = d.nombreDepto
                        });
        foreach (var dato in joinList)
        {
            Console.WriteLine("{0} es empleado del departamento de {1}", dato.nombreEmpleado, dato.nombreDepto);
        }
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top