哪一个更好的做法:将整个对象列表传递给方法,或者创建汇总对象列表的新实例?

时间:2016-04-06 15:17:03

标签: c# design-patterns

我的问题是当我有对象列表时,我应该将整个列表传递给另一层中的方法,还是应该有另一个模型并制作新的列表?

namespace Model
{
  Public Class class2
   {
     blah blah properties
   }
  Public Class Person
   {
     public string firstname { get; set; }
     public string lastname {get; set;}
     public int age {get; set;}
     public Datetime regDate {get; set;}
     public class2 SomeClass{get; set;}
   }
}

Namespace Service
{ 
  Namespace Model
   {
    public class LogPerson
    {
      public string firstName {get; set;}
      public string lastName {get; set;}
      public int age {get; set;}
    }
   }

public static class Log
 {
    //Method1
    public void Log(List<Person> persons)
      { 
         LogProvider.Log(Persons.Select(p=> new LogPerson{ p.firstName,   p.lastname, p.age}).ToList());
      }

     //Method2
     public void Log(List<LogPerson> persons)
     {
        LogProvider.Log(persons);
     }
}

正如我上面所示,传递完整的人员对象列表(日志提供者只需要名字,姓氏和年龄)或使用从一开始就需要LogPerson对象的方法2是一种好习惯。

我不需要意见,我需要的是,根据关注点的分离,哪一个是正确的方式?

2 个答案:

答案 0 :(得分:2)

如果您定义的接口具有您需要记录的属性,则只能有一个模型:

// keep in a separate, shared assembly
public interface ILoggablePerson {
  string firstName {get; set;}
  string lastName {get; set;}
  int age {get; set;}
}

然后

 public void Log(IEnumerable<ILoggablePerson> people)
 {
    LogProvider.Log(persons);
 }

(注意使用IEnumerable而不是List:这意味着参数可以延迟评估[如果需要]并且不会将方法使用者与特定数据结构联系起来)

最后,您的Person类只需将该接口添加到其声明:

public class Person : ILoggablePerson

然后你可以将Person对象传递给你的记录器类:

Log.Log(new Person[] { ... }); // etc.

修改以下评论

以下是我如何解决这个问题的一个例子:接口仍然提供关注点分离,只使用依赖注入,提供者和工厂模式......注释内联,希望它有所帮助。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Service;
using Model;
using System.Collections.Concurrent;

namespace ConsoleApplication8
{
    class Program
    {
        static void Main(string[] args)
        {
            var carLogger = Logger.Default(new Model.CarLogEntryProvider()); // generic type inference
            // want to log to a file? => Logger.File(@"c:\file path", new Model.CarLogEntryProvider()); 

            var personLogger = Logger.Default(new Model.PersonLogger());

            Car c1 = new Car() { Make = "Toyota", Registration = "ABC123" };
            Car c2 = new Car() { Make = "Toyota", Registration = "ABX127" };

            carLogger.AddEntries(new Car[] { c1, c2 });

            Person p1 = new Person() { Age = 21, FirstName = "Tony", LastName = "Baloney" };
            Person p2 = new Person() { Age = 31, FirstName = "Mary", LastName = "O'Doherty" };

            personLogger.AddEntry(p1);
            personLogger.AddEntry(p2);

            Console.ReadLine();
        }
    }
}

// model namespace knows how the model works and can make the decision of how its types are logged
// by implementing ILogEntryProvider as required: can even combine fields, add additional fields (eg timestamp) etc.
namespace Model
{

    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int Age { get; set; }
    }

    public class Car
    {
        public string Make { get; set; }
        public string Registration { get; set; }
    }

    // knows how to log a Car as required by this namespace: another system can implement this differently
    class CarLogEntryProvider : ILogEntryProvider<Car>
    {

        public ILogEntry GetLogEntry(Car car)
        {
            var result = new BasicLogEntry(); // can use a ThreadSafeLogEntry if application is multi-threaded
            result.Values["make"] = car.Make;
            result.Values["reg"] = car.Registration;
            return result;
        }
    }

    // knows how to log a Car as required by this namespace: another system can implement this differently
    class PersonLogger : ILogEntryProvider<Person>
    {
        public ILogEntry GetLogEntry(Person person)
        {
            var result = new BasicLogEntry(); // can use a ThreadSafeLogEntry if application is multi-threaded
            result.Values["age"] = person.Age.ToString();
            result.Values["surname"] = person.LastName;
            return result;
        }
    }
}

// service namespace has no knowledge of the model, it just provides interfaces for the model to provide
namespace Service
{
    public interface ILogEntry {
        IDictionary<string, string> Values { get; }
    }

    public interface ILogEntryProvider<T>
    {
        // can add any other properties here for fields which are always required

        ILogEntry GetLogEntry(T itemToLog);
    }


    public class ThreadSafeLogEntry : ILogEntry
    {
        public ThreadSafeLogEntry() { Values = new ConcurrentDictionary<string, string>(); }

        public IDictionary<string, string> Values
        {
            get;
            set;
        }
    }

    public class BasicLogEntry : ILogEntry
    {
        public BasicLogEntry() { Values = new Dictionary<string, string>(); }

        public IDictionary<string, string> Values
        {
            get;
            set;
        }
    }

    public interface ILogger<T>
    {
        void AddEntry(T item);
        void AddEntries(IEnumerable<T> items);
    }

    // factory pattern
    public static class Logger
    {
        public static ILogger<T> Default<T>(ILogEntryProvider<T> entryProvider)
        {
            return new ConsoleLogger<T>(entryProvider);
        }

        // create other methods here as required, all returning type ILogger<T>
        // eg:  public static ILogger<T> File(string filePath, ILogEntryProvider<T> entryProvider) { ... }
    }

    class ConsoleLogger<T> : ILogger<T>
    {
        private ILogEntryProvider<T> logEntryProvider;

        public ConsoleLogger(ILogEntryProvider<T> logEntryProvider)  // dependency injection
        {
            if (logEntryProvider == null)
                throw new ArgumentNullException();

            this.logEntryProvider = logEntryProvider;
        }

        void ILogger<T>.AddEntry(T item)    // explicit interface implementation: discourage use of this class in a fashion which doesn't treat it as an interface type
        {
            ((ILogger<T>)this).AddEntries(new T[] { item });
        }

        void ILogger<T>.AddEntries(IEnumerable<T> items)    // explicit interface implementation: discourage use of this class in a fashion which doesn't treat it as an interface type
        {
            var entries = items.Select(item => logEntryProvider.GetLogEntry(item))
                .Where(anyEntry => anyEntry != null) // perhaps a different behaviour required here...
                    .Select(nonNullEntry => nonNullEntry.Values);

            foreach(var entry in entries)
            {
                Console.WriteLine("New Entry: {0}", typeof(T).Name);
                foreach(var property in entry.Keys)
                {
                    // record each string pair etc. etc
                    string propertyValue = entry[property];
                    Console.WriteLine("[{0}] = \"{1}\"", property, propertyValue);
                }
                Console.WriteLine();
            }
        }

        // TO DO: create an async pattern method:
        // public static Task AddEntryAsync<T>(ILogEntryProvider<T> adapterFunc, IEnumerable<T> items) { .... }
    }
}

答案 1 :(得分:0)

如果您想要分离关注点的答案,如果您不需要传递的属性,那么就可以创建另一个模型和新列表了。

这将分离各层的关注点。第2层不关心第1层不需要的属性。