Kimlik veya Nesne Geçiş?


38

Bir etki alanı varlığı elde etmek için bir iş mantığı yöntemi sağlarken, parametre bir nesneyi mi yoksa bir kimliği mi kabul etmeli? Örneğin, şunu yapmalı mıyız:

public Foo GetItem(int id) {}

veya bu:

public Foo GetItem(Foo foo) {}

Nesnelerin etrafından dolaşmalarına, bütünlüklerine inanıyorum, peki ya bu nesneyi elde ettiğimiz ve sadece kimliği bildiğimiz bu durum? Arayan boş bir Foo oluşturup kimliği ayarlamalı mı, yoksa kimliği yalnızca yönteme mi geçirmeli? Gelen Foo boş olacağından, Kimlik hariç, arayan kişinin bir Foo oluşturması ve kimliği yalnızca GetItem () yöntemine kimliği gönderebilmesi için kimliğini ayarlaması gerektiğini görmüyorum.

Yanıtlar:


42

Arama için kullanılan tek alan.

Arayan kişi sahip değil, bir Footane almaya çalışıyor. Tabii, Fooboş bırakılan diğer tüm alanlarla geçici yapabilirsiniz , ancak bu yalnızca önemsiz veri yapıları için işe yarar. Çoğu nesnede, çoğunlukla boş nesne yaklaşımıyla ihlal edilecek değişmezler vardır, bu yüzden kaçının.


Teşekkür ederim. Amiram'in cevabındaki 2 numaralı puanla bu cevabı seviyorum.
Bob Horn,

3
Bu mantıklı görünüyor. Ancak performans açısından, arayan kişinin nesneye sahip olabileceği ve bulunmayabileceği alanlara rastladım. Yalnızca kimliği iletmek, söz konusu nesnenin veritabanından iki kez okunmasına neden olabilir. Bu sadece kabul edilebilir bir performans mıdır? Yoksa hem kimliğin hem de geçirilecek nesnenin olasılığını sağlıyor musunuz?
computrius,

Bu günlerde 'nesneyi asla geçme' kurallarını bir tuz teli ile alıyorum. Bu sadece sizin içerik / senaryoya bağlıdır.
Bruno

12

Bu herhangi bir zamanda veya gelecekte tel üzerinden (seri hale getirilmiş / seri hale getirilmiş) geçecek mi? Tek kimlik türünü who-know-how-how-full tam nesnesi üzerinde destekleyin.

Kimliğin varlığının güvenliğini sağlamak için arıyorsanız, o zaman da kod çözümleri var. Bir örneğe ihtiyacınız olursa haberim olsun.

Düzenleme: Kimlik tipinin güvenliği konusunda genişleyen:

Öyleyse, yöntemine bakalım:

public Foo GetItem(int id) {}

Yalnızca iletilen tamsayının bir nesne için olduğunu umuyoruz . Birisi onu kötüye kullanabilir ve bazı nesnelerin tamsayı kimliğini ya da sadece elle yazarak geçebilir . Güvenli değil . İkincisi, pass'ı bir nesne sürümü kullansanız bile , birinin değiştirebileceği bir kimlik alanına sahip olduğumdan eminim . Ve son olarak, eğer bunlar sadece bir dönüş tipi değiştiğinden bir sınıfta bunlar mevcutsa, metot aşırı yükünü kullanamazsınız. Bu yöntemi C # 'da type-safe bakmak için biraz yeniden yazalım:idFooBar812341FooFooFooint

public Foo GetItem(IntId<Foo> id) {}

Bu yüzden IntIdona genel bir parçası olan bir sınıf tanıttım . Bu özel durumda, sadece intbununla ilişkili olanı istiyorum Foo. Sadece çıplak olarak geçemem intya IntId<Bar>da yanlışlıkla ona bir şey atamam. Aşağıda bu tip güvenli tanımlayıcıları nasıl yazdım. Gerçek yatan manipülasyonu, germe notu mı intolduğunu sadece veri erişim katmanında. Yukarıda sadece güçlü tipi gören ve iç intkimliğine (doğrudan) erişimi olmayan herhangi bir şey . Bunun için bir sebep olmamalı.

IModelId.cs arayüzü:

namespace GenericIdentifiers
{
    using System.Runtime.Serialization;
    using System.ServiceModel;

    /// <summary>
    /// Defines an interface for an object's unique key in order to abstract out the underlying key
    /// generation/maintenance mechanism.
    /// </summary>
    /// <typeparam name="T">The type the key is representing.</typeparam>
    [ServiceContract]
    public interface IModelId<T> where T : class
    {
        /// <summary>
        /// Gets a string representation of the domain the model originated from.
        /// </summary>
        /// <value>The origin.</value>
        [DataMember]
        string Origin
        {
            [OperationContract]get;
        }

        /// <summary>
        /// The model instance identifier for the model object that this <see cref="IModelId{T}"/> refers to.
        /// Typically, this is a database key, file name, or some other unique identifier.
        /// <typeparam name="TKeyDataType">The expected data type of the identifier.</typeparam>
        /// </summary>
        /// <typeparam name="TKeyDataType">The expected data type of the identifier.</typeparam>
        /// <returns>The unique key as the data type specified.</returns>
        [OperationContract]
        TKeyDataType GetKey<TKeyDataType>();

        /// <summary>
        /// Performs an equality check on the two model identifiers and returns <c>true</c> if they are equal; otherwise
        /// <c>false</c> is returned.  All implementations must also override the equal operator.
        /// </summary>
        /// <param name="obj">The identifier to compare against.</param>
        /// <returns><c>true</c> if the identifiers are equal; otherwise <c>false</c> is returned.</returns>
        [OperationContract]
        bool Equals(IModelId<T> obj);
    }
}

ModelIdBase.cs ana sınıfı:

namespace GenericIdentifiers
{
    using System;
    using System.Collections.Generic;
    using System.Runtime.Serialization;

    /// <summary>
    /// Represents an object's unique key in order to abstract out the underlying key generation/maintenance mechanism.
    /// </summary>
    /// <typeparam name="T">The type the key is representing.</typeparam>
    [DataContract(IsReference = true)]
    [KnownType("GetKnownTypes")]
    public abstract class ModelIdBase<T> : IModelId<T> where T : class
    {
        /// <summary>
        /// Gets a string representation of the domain the model originated from.
        /// </summary>
        [DataMember]
        public string Origin
        {
            get;

            internal set;
        }

        /// <summary>
        /// The model instance identifier for the model object that this <see cref="ModelIdBase{T}"/> refers to.
        /// Typically, this is a database key, file name, or some other unique identifier.
        /// </summary>
        /// <typeparam name="TKeyDataType">The expected data type of the identifier.</typeparam>
        /// <returns>The unique key as the data type specified.</returns>
        public abstract TKeyDataType GetKey<TKeyDataType>();

        /// <summary>
        /// Performs an equality check on the two model identifiers and returns <c>true</c> if they are equal;
        /// otherwise <c>false</c> is returned. All implementations must also override the equal operator.
        /// </summary>
        /// <param name="obj">The identifier to compare against.</param>
        /// <returns>
        ///   <c>true</c> if the identifiers are equal; otherwise <c>false</c> is returned.
        /// </returns>
        public abstract bool Equals(IModelId<T> obj);

        protected static IEnumerable<Type> GetKnownTypes()
        {
            return new[] { typeof(IntId<T>), typeof(GuidId<T>) };
        }
    }
}

IntId.cs:

namespace GenericIdentifiers
{
    // System namespaces
    using System;
    using System.Diagnostics;
    using System.Globalization;
    using System.Runtime.Serialization;

    /// <summary>
    /// Represents an abstraction of the database key for a Model Identifier.
    /// </summary>
    /// <typeparam name="T">The expected owner data type for this identifier.</typeparam>
    [DebuggerDisplay("Origin={Origin}, Integer Identifier={Id}")]
    [DataContract(IsReference = true)]
    public sealed class IntId<T> : ModelIdBase<T> where T : class
    {
        /// <summary>
        /// Gets or sets the unique ID.
        /// </summary>
        /// <value>The unique ID.</value>
        [DataMember]
        internal int Id
        {
            get;

            set;
        }

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="intIdentifier1">The first Model Identifier to compare.</param>
        /// <param name="intIdentifier2">The second Model Identifier to compare.</param>
        /// <returns>
        ///   <c>true</c> if the instances are equal; otherwise <c>false</c> is returned.
        /// </returns>
        public static bool operator ==(IntId<T> intIdentifier1, IntId<T> intIdentifier2)
        {
            return object.Equals(intIdentifier1, intIdentifier2);
        }

        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="intIdentifier1">The first Model Identifier to compare.</param>
        /// <param name="intIdentifier2">The second Model Identifier to compare.</param>
        /// <returns>
        ///   <c>true</c> if the instances are equal; otherwise <c>false</c> is returned.
        /// </returns>
        public static bool operator !=(IntId<T> intIdentifier1, IntId<T> intIdentifier2)
        {
            return !object.Equals(intIdentifier1, intIdentifier2);
        }

        /// <summary>
        /// Performs an implicit conversion from <see cref="IntId{T}"/> to <see cref="System.Int32"/>.
        /// </summary>
        /// <param name="id">The identifier.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator int(IntId<T> id)
        {
            return id == null ? int.MinValue : id.GetKey<int>();
        }

        /// <summary>
        /// Performs an implicit conversion from <see cref="System.Int32"/> to <see cref="IntId{T}"/>.
        /// </summary>
        /// <param name="id">The identifier.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator IntId<T>(int id)
        {
            return new IntId<T> { Id = id };
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current
        /// <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current
        /// <see cref="T:System.Object"/>.</param>
        /// <returns>true if the specified <see cref="T:System.Object"/> is equal to the current
        /// <see cref="T:System.Object"/>; otherwise, false.</returns>
        /// <exception cref="T:System.NullReferenceException">The <paramref name="obj"/> parameter is null.</exception>
        public override bool Equals(object obj)
        {
            return this.Equals(obj as IModelId<T>);
        }

        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        public override int GetHashCode()
        {
            unchecked
            {
                var hash = 17;

                hash = (23 * hash) + (this.Origin == null ? 0 : this.Origin.GetHashCode());
                return (31 * hash) + this.GetKey<int>().GetHashCode();
            }
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return this.Origin + ":" + this.GetKey<int>().ToString(CultureInfo.InvariantCulture);
        }

        /// <summary>
        /// Performs an equality check on the two model identifiers and returns <c>true</c> if they are equal;
        /// otherwise <c>false</c> is returned.  All implementations must also override the equal operator.
        /// </summary>
        /// <param name="obj">The identifier to compare against.</param>
        /// <returns>
        ///   <c>true</c> if the identifiers are equal; otherwise <c>false</c> is returned.
        /// </returns>
        public override bool Equals(IModelId<T> obj)
        {
            if (obj == null)
            {
                return false;
            }

            return (obj.Origin == this.Origin) && (obj.GetKey<int>() == this.GetKey<int>());
        }

        /// <summary>
        /// The model instance identifier for the model object that this <see cref="ModelIdBase{T}"/> refers to.
        /// Typically, this is a database key, file name, or some other unique identifier.
        /// </summary>
        /// <typeparam name="TKeyDataType">The expected data type of the identifier.</typeparam>
        /// <returns>The unique key as the data type specified.</returns>
        public override TKeyDataType GetKey<TKeyDataType>()
        {
            return (TKeyDataType)Convert.ChangeType(this.Id, typeof(TKeyDataType), CultureInfo.InvariantCulture);
        }

        /// <summary>
        /// Generates an object from its string representation.
        /// </summary>
        /// <param name="value">The value of the model's type.</param>
        /// <returns>A new instance of this class as it's interface containing the value from the string.</returns>
        internal static ModelIdBase<T> FromString(string value)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }

            int id;
            var originAndId = value.Split(new[] { ":" }, StringSplitOptions.None);

            if (originAndId.Length != 2)
            {
                throw new ArgumentOutOfRangeException("value", "value must be in the format of Origin:Identifier");
            }

            return int.TryParse(originAndId[1], NumberStyles.None, CultureInfo.InvariantCulture, out id)
                ? new IntId<T> { Id = id, Origin = originAndId[0] }
                : null;
        }
    }
}

ve, kod tabanımı tamamlama uğruna, bir tane de GUID varlıkları olan GuidId.cs:

namespace GenericIdentifiers
{
    // System namespaces
    using System;
    using System.Diagnostics;
    using System.Globalization;
    using System.Runtime.Serialization;

    /// <summary>
    /// Represents an abstraction of the database key for a Model Identifier.
    /// </summary>
    /// <typeparam name="T">The expected owner data type for this identifier.</typeparam>
    [DebuggerDisplay("Origin={Origin}, GUID={Id}")]
    [DataContract(IsReference = true)]
    public sealed class GuidId<T> : ModelIdBase<T> where T : class
    {
        /// <summary>
        /// Gets or sets the unique ID.
        /// </summary>
        /// <value>The unique ID.</value>
        [DataMember]
        internal Guid Id
        {
            get;

            set;
        }

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="guidIdentifier1">The first Model Identifier to compare.</param>
        /// <param name="guidIdentifier2">The second Model Identifier to compare.</param>
        /// <returns>
        ///   <c>true</c> if the instances are equal; otherwise <c>false</c> is returned.
        /// </returns>
        public static bool operator ==(GuidId<T> guidIdentifier1, GuidId<T> guidIdentifier2)
        {
            return object.Equals(guidIdentifier1, guidIdentifier2);
        }

        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="guidIdentifier1">The first Model Identifier to compare.</param>
        /// <param name="guidIdentifier2">The second Model Identifier to compare.</param>
        /// <returns>
        ///   <c>true</c> if the instances are equal; otherwise <c>false</c> is returned.
        /// </returns>
        public static bool operator !=(GuidId<T> guidIdentifier1, GuidId<T> guidIdentifier2)
        {
            return !object.Equals(guidIdentifier1, guidIdentifier2);
        }

        /// <summary>
        /// Performs an implicit conversion from <see cref="GuidId{T}"/> to <see cref="System.Guid"/>.
        /// </summary>
        /// <param name="id">The identifier.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator Guid(GuidId<T> id)
        {
            return id == null ? Guid.Empty : id.GetKey<Guid>();
        }

        /// <summary>
        /// Performs an implicit conversion from <see cref="System.Guid"/> to <see cref="GuidId{T}"/>.
        /// </summary>
        /// <param name="id">The identifier.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator GuidId<T>(Guid id)
        {
            return new GuidId<T> { Id = id };
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current
        /// <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current
        /// <see cref="T:System.Object"/>.</param>
        /// <returns>true if the specified <see cref="T:System.Object"/> is equal to the current
        /// <see cref="T:System.Object"/>; otherwise, false.</returns>
        /// <exception cref="T:System.NullReferenceException">The <paramref name="obj"/> parameter is null.</exception>
        public override bool Equals(object obj)
        {
            return this.Equals(obj as IModelId<T>);
        }

        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        public override int GetHashCode()
        {
            unchecked
            {
                var hash = 17;

                hash = (23 * hash) + (this.Origin == null ? 0 : this.Origin.GetHashCode());
                return (31 * hash) + this.GetKey<Guid>().GetHashCode();
            }
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return this.Origin + ":" + this.GetKey<Guid>();
        }

        /// <summary>
        /// Performs an equality check on the two model identifiers and returns <c>true</c> if they are equal;
        /// otherwise <c>false</c> is returned.  All implementations must also override the equal operator.
        /// </summary>
        /// <param name="obj">The identifier to compare against.</param>
        /// <returns>
        ///   <c>true</c> if the identifiers are equal; otherwise <c>false</c> is returned.
        /// </returns>
        public override bool Equals(IModelId<T> obj)
        {
            if (obj == null)
            {
                return false;
            }

            return (obj.Origin == this.Origin) && (obj.GetKey<Guid>() == this.GetKey<Guid>());
        }

        /// <summary>
        /// The model instance identifier for the model object that this <see cref="ModelIdBase{T}"/> refers to.
        /// Typically, this is a database key, file name, or some other unique identifier.
        /// </summary>
        /// <typeparam name="TKeyDataType">The expected data type of the identifier.</typeparam>
        /// <returns>The unique key as the data type specified.</returns>
        public override TKeyDataType GetKey<TKeyDataType>()
        {
            return (TKeyDataType)Convert.ChangeType(this.Id, typeof(TKeyDataType), CultureInfo.InvariantCulture);
        }

        /// <summary>
        /// Generates an object from its string representation.
        /// </summary>
        /// <param name="value">The value of the model's type.</param>
        /// <returns>A new instance of this class as it's interface containing the value from the string.</returns>
        internal static ModelIdBase<T> FromString(string value)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }

            Guid id;
            var originAndId = value.Split(new[] { ":" }, StringSplitOptions.None);

            if (originAndId.Length != 2)
            {
                throw new ArgumentOutOfRangeException("value", "value must be in the format of Origin:Identifier");
            }

            return Guid.TryParse(originAndId[1], out id) ? new GuidId<T> { Id = id, Origin = originAndId[0] } : null;
        }
    }
}

Evet, tel üzerinden gidiyor. Kimlik için kimliğinin tip güvenliğine ihtiyacım olduğunu bilmiyorum, ama bununla ne kastettiğinizi görmek istiyorum. Yani evet, bunu genişletirseniz, bu iyi olurdu.
Bob Horn,

Ben de yaptım Biraz kod ağırlıklı oldu :)
Jesse C. Slicer

1
Bu arada, Originözelliği açıklamadım : SQL Server parlance'daki bir şemaya çok benziyor. Bir olabilir Foosizin Muhasebe yazılımında kullanılan bu ve başka Fooİnsan Kaynakları ve küçük alan, veri erişim katmanında ayırt etmek var olduğunu olduğunu. Ya da herhangi bir çatışma yoksa, benim yaptığım gibi görmezden gel.
Jesse C. Dilimleyici

1
@ JesseC.Slicer: İlk bakışta, fazla mühendislik için mükemmel bir örnek gibi görünüyor.
Doktor Brown

2
@DocBrown her birine kendi omuz silkti . Bazı insanların ihtiyaç duyduğu bir çözüm. Bazı insanlar yapmaz. YAGNI ise, o zaman kullanmayın. İhtiyacın varsa, işte orada.
Jesse C. Dilimleyici

5

Sonuçlarına kesinlikle katılıyorum. Bazı nedenlerden dolayı bir kimlik iletmek tercih edilir:

  1. Basit. Bileşenler arasındaki arayüz basit olmalıdır.
  2. FooYalnızca kimlik için bir nesne oluşturmak, yanlış değerler oluşturmak anlamına gelir. Birisi bir hata yapabilir ve bu değerleri kullanabilir.
  3. intdaha platform çapında ve tüm modern dillerde yerel olarak ilan edilebilir. FooYöntem arayan tarafından bir nesne oluşturmak için , muhtemelen karmaşık bir veri yapısı (json nesnesi gibi) oluşturmanız gerekecektir.

4

Ben Voigt’in önerdiği gibi nesnenin tanımlayıcısını aramaya başlamanın akıllıca olacağını düşünüyorum.

Ancak, nesnenizin tanımlayıcısının türünün değişebileceğini unutmayın. Dolayısıyla, öğelerimden her biri için bir tanımlayıcı sınıfı oluşturacağım ve yalnızca bu tanımlayıcıların bu örnekleri aracılığıyla öğeleri aramaya izin vereceğim. Aşağıdaki örneğe bakın:

public class Item
{
  public class ItemId
  {
    public int Id { get; set;}
  }

  public ItemId Id; { get; set; }
}

public interface Service
{
  Item GetItem(ItemId id);
}

Ben kapsülleme kullandım, ama sizden Itemmiras alabilirsiniz ItemId.

Bu şekilde, kimliğinizin türü yol boyunca değişirse, Itemsınıfta veya GetItem yönteminin imzasında hiçbir şey değiştirmeniz gerekmez . Yalnızca hizmetin uygulanmasında kodunuzu değiştirmeniz gerekir (bu, her durumda değişen tek şey budur).


2

Metodunun ne yaptığına bağlı .

Genel olarak Get methods, id parameternesneyi geri almak ve geri almak ortak bir anlam ifade eder . Güncelleme için veya SET methodstüm nesne ayarlanacak / güncellenecek gönderilir.

Sizin method is passing search parameters(bireysel ilkel türlerin bir koleksiyonu olarak) bir sonuç kümesi elde ettiğiniz diğer bazı durumlarda use a container to hold, arama parametrelerinize göre akıllıca olabilir . Bu, uzun vadede parametrelerin sayısı değişecekse faydalıdır. Böylece, sen would not needdeğiştirmek için signature of your method, add or remove parameter in all over the places.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.