只读一流的设计当一个非只读类是已经到位 [英] readonly class design when a non-readonly class is already in place

查看:192
本文介绍了只读一流的设计当一个非只读类是已经到位的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个类,构造时,负载是从数据库中的信息。该信息是所有的修改,然后开发者可以在其上调用保存(),使其保存这些信息反馈到数据库中。

I have a class that upon construction, loads it's info from a database. The info is all modifiable, and then the developer can call Save() on it to make it Save that information back to the database.

我也创建一个类,将从数据库负荷,但不会允许任何更新它。 (只读版本)。我的问题是,我应该做一个单独的类,继承,或者我应该只需要更新现有对象采取在构造一个只读参数,或者我应该做一个单独的类完全?

I am also creating a class that will load from the database, but won't allow any updates to it. (a read only version.) My question is, should I make a separate class and inherit, or should I just update the existing object to take a readonly parameter in the constructor, or should I make a separate class entirely?

现有的类已经在代码中的许多地方使用。

The existing class is already used in many places in the code.

感谢。

更新:

首先,有很多伟大这里的答案。这将是很难接受只有一个。 。谢谢大家。

Firstly, there's a lot of great answers here. It would be hard to accept just one. Thanks everyone.

似乎存在的主要问题有:

    $ B $基于类名和继承结构b
  • 会议的预期。

  • 防止不必要的重复代码

  • Meeting expectations based on class names and inheritance structures.
  • Preventing unnecessary duplicate code

有似乎是可读和只读之间有很大的区别。只读类应该不会被继承。但可读类表明,它也可能会在某个时候获得可写

There seems to be a big difference between Readable and ReadOnly. A Readonly class should probably not be inherited. But a Readable class suggests that it might also gain writeability at some point.

于是左思右想,这里是我在想什么:

So after much thought, here's what I'm thinking:

public class PersonTestClass
{
    public static void Test()
    {

        ModifiablePerson mp = new ModifiablePerson();
        mp.SetName("value");
        ReadOnlyPerson rop = new ReadOnlyPerson();
        rop.GetName();
        //ReadOnlyPerson ropFmp = (ReadOnlyPerson)mp;  // not allowed.
        ReadOnlyPerson ropFmp = (ReadOnlyPerson)(ReadablePerson)mp; 
          // above is allowed at compile time (bad), not at runtime (good).
        ReadablePerson rp = mp;
    }
}

public class ReadablePerson
{
    protected string name;
    public string GetName()
    {
        return name;
    }        
}
public sealed class ReadOnlyPerson : ReadablePerson
{
}
public class ModifiablePerson : ReadablePerson
{
    public void SetName(string value)
    {
        name = value;
    }
}



不幸的是,我还不知道该怎么办这与属性(见StriplingWarrior对这个有特性做了回答),但我有一种感觉它会涉及到保护和关键字的非对称特性的访问修饰符的。

另外,幸运的是,该数据是从数据库加载不必变成参考对象,而它们是简单类型。这意味着,我真的不担心的人修改 ReadOnlyPerson 对象的成员。

Also, fortunately for me, the data that is loaded from the database does not have to be turned into reference objects, rather they are simple types. This means I don't really have to worry about people modifying the members of the ReadOnlyPerson object.

更新2:

请注意,由于StriplingWarrior曾建议,向下转换会导致一些问题,但这通常是铸造猴子真实动物回落到狗可不好。然而,似乎即使铸件被允许在编译的时候,它实际上没有允许在运行时。

Note, as StriplingWarrior has suggested, downcasting can lead to problems, but this is generally true as casting a Monkey to and Animal back down to a Dog can be bad. However, it seems that even though the casting is allowed at compile time, it is not actually allowed at runtime.

一个包装类也可以做的伎俩,但我喜欢这更好,因为它避免了深拷贝传入的对象的问题/允许传入的对象被如此修改修改包装类。

A wrapper class may also do the trick, but I like this better because it avoids the problem of having to deep copy the passed in object / allow the passed in object to be modified thus modifying the wrapper class.

推荐答案

rel=\"nofollow\">里氏替换原则的说,你不应该让你只读类从您的读写类继承,因为消费类必须意识到,他们不能调用其Save方法没有得到一个例外。

The Liskov Substitution Principle says that you shouldn't make your read-only class inherit from your read-write class, because consuming classes would have to be aware that they can't call the Save method on it without getting an exception.

制作可写的类扩展可读类会更有意义对我来说,只要没有什么上可读类,它指示它的对象永远不能持续。例如,我不会调用基类中的只读[无论] ,因为如果你有需要的方法的 ReadOnlyPerson 作为一个参数,该方法将在假设,这将是不可能的事情,他们做的那个对象有数据库,这未必是真实的任何影响有道理,如果实际实例是 WriteablePerson

Making the writable class extend the readable class would make more sense to me, as long as there is nothing on the readable class that indicates its object can never be persisted. For example, I wouldn't call the base class a ReadOnly[Whatever], because if you have a method that takes a ReadOnlyPerson as an argument, that method would be justified in assuming that it would be impossible for anything they do to that object to have any impact on the database, which is not necessarily true if the actual instance is a WriteablePerson.

我本来假设在你读只有一流的,你只是想阻止人们调用保存方法。根据我所看到的在你的答案,回答您的问题(这实际上应该是对你的问题的最新情况,顺便说一句),这里是你可能要遵循一个模式:

I was originally assuming that in your read-only class you only wanted to prevent people calling the Save method. Based on what I'm seeing in your answer-response to your question (which should actually be an update on your question, by the way), here's a pattern you might want to follow:

public abstract class ReadablePerson
{

    public ReadablePerson(string name)
    {
        Name = name;
    }

    public string Name { get; protected set; }

}

public sealed class ReadOnlyPerson : ReadablePerson
{
    public ReadOnlyPerson(string name) : base(name)
    {
    }
}

public sealed class ModifiablePerson : ReadablePerson
{
    public ModifiablePerson(string name) : base(name)
    {
    }
    public new string Name { 
        get {return base.Name;}
        set {base.Name = value; }
    }
}

这确保了真正的 ReadOnlyPerson 不能简单地转换为ModifiablePerson和修改。如果你愿意相信,开发商不会去尝试向下转换参数以这种方式,不过,我更喜欢史蒂夫和奥利弗的回答基于接口的方法。

This ensures that a truly ReadOnlyPerson cannot simply be cast as a ModifiablePerson and modified. If you're willing to trust that developers won't try to down-cast arguments in this way, though, I prefer the interface-based approach in Steve and Olivier's answers.

另一种选择是让你的 ReadOnlyPerson 只是一个人的包装类对象。这将需要更多的样板代码,但它派上用场的时候你不能改变的基类。

Another option would be to make your ReadOnlyPerson just be a wrapper class for a Person object. This would necessitate more boilerplate code, but it comes in handy when you can't change the base class.

最后一点,因为你喜欢学习的里氏替换原则:通过具有Person类是负责加载自己从数据库中,你打破了单一职责原则。理想情况下,你的Person类也有属性来表示包括数据人,并会有不同的类(也许 PersonRepository ),这是负责生产的人从数据库或保存一个人到数据库中。

One last point, since you enjoyed learning about the Liskov Substitution Principle: By having the Person class be responsible for loading itself out of the database, you are breaking the Single-Responsibility Principle. Ideally, your Person class would have properties to represent the data that comprises a "Person," and there would be a different class (maybe a PersonRepository) that's responsible for producing a Person from the database or saving a Person to the database.

在回答您的意见:


  • 虽然技术上可以回答你自己的问题,计算器主要是从其他人得到的答案。这就是为什么它不会让你接受你自己的答案,直到一定的缓冲期已经过去了。我们鼓励您改进你的问题,直到有人想出了一个适当的解决您最初的问题回应的意见和解答。

  • 我发的 ReadablePerson 摘要,因为它似乎是你永远只希望创建一个只读或一个可写的人。即使两个子类可以被认为是一个ReadablePerson,这将是创建一个新ReadablePerson()时,你可以很容易地创建一个<$点C $ C>新ReadOnlyPerson()?使得类的抽象要求用户实例化时,他们选择了两个子类中的一个。

  • 系统PersonRepository将那种像一个工厂,但资源库表示你重新实际上是从一些数据源提取人的信息,而不是创造者凭空。

  • 在我的脑海中,Person类将只是一个POCO,没有逻辑吧:仅仅属性。该库将负责建设Person对象。与其说:

  • While you can technically answer your own question, StackOverflow is largely about getting answers from other people. That's why it won't let you accept your own answer until a certain grace period has passed. You are encouraged to refine your question and respond to comments and answers until someone has come up with an adequate solution to your initial question.
  • I made the ReadablePerson class abstract because it seemed like you'd only ever want to create a person that is read-only or one that is writeable. Even though both of the child classes could be considered to be a ReadablePerson, what would be the point of creating a new ReadablePerson() when you could just as easily create a new ReadOnlyPerson()? Making the class abstract requires the user to choose one of the two child classes when instantiating them.
  • A PersonRepository would sort of be like a factory, but the word "repository" indicates that you're actually pulling the person's information from some data source, rather than creating the person out of thin air.
  • In my mind, the Person class would just be a POCO, with no logic in it: just properties. The repository would be responsible for building the Person object. Rather than saying:

// This is what I think you had in mind originally
var p = new Person(personId);



...并允许Person对象转到数据库来填充它的各种属性,你会说:

... and allowing the Person object to go to the database to populate its various properties, you would say:

// This is a better separation of concerns
var p = _personRepository.GetById(personId);



然后PersonRepository会得到相应的信息从数据库中,并用这些数据构建的人。

The PersonRepository would then get the appropriate information out of the database and construct the Person with that data.

如果你想打电话说没有理由改变人的方法,你可以通过将其转换为只读包装保护的改变那个人(以下模式的.NET库遵循与 ReadOnlyCollection还< T> 类)。另一方面,需要一个可写对象的方法,可以给出的直接

If you wanted to call a method that has no reason to change the person, you could protect that person from changes by converting it to a Readonly wrapper (following the pattern that the .NET libraries follow with the ReadonlyCollection<T> class). On the other hand, methods that require a writeable object could be given the Person directly:

var person = _personRepository.GetById(personId);
// Prevent GetVoteCount from changing any of the person's information
int currentVoteCount = GetVoteCount(person.AsReadOnly()); 
// This is allowed to modify the person. If it does, save the changes.
if(UpdatePersonDataFromLdap(person))
{
     _personRepository.Save(person);
}


  • 使用接口的好处是,你不能强迫特定的类层次结构。这将使你在未来能有更好的灵活性。例如,让我们说,你这样写你的方法的时刻:

  • The benefit of using interfaces is that you're not forcing a specific class hierarchy. This will give you better flexibility in the future. For example, let's say that for the moment you write your methods like this:

    GetVoteCount(ReadablePerson p);
    UpdatePersonDataFromLdap(ReadWritePerson p);
    



    ...但随后在两年你决定改变到包装实现。突然 ReadOnlyPerson 不再是 ReadablePerson ,因为它是一个包装类,而不是一个基类的一个扩展。你更改 ReadablePerson ReadOnlyPerson 中的所有方法签名?

    ... but then in two years you decide to change to the wrapper implementation. Suddenly ReadOnlyPerson is no longer a ReadablePerson, because it's a wrapper class instead of an extension of a base class. Do you change ReadablePerson to ReadOnlyPerson in all your method signatures?

    或者说你决定把事情简单化,只是所有的类合并成一个单一的类:现在你有改变所有的方法,只是采取Person对象。在另一方面,如果你已经编程接口:

    Or say you decide to simplify things and just consolidate all your classes into a single Person class: now you have to change all your methods to just take Person objects. On the other hand, if you had programmed to interfaces:

    GetVoteCount(IReadablePerson p);
    UpdatePersonDataFromLdap(IReadWritePerson p);
    



    ...那么这些方法不关心你的对象层次结构看起来像什么,只要对象你给他们实现他们要求的接口。您可以随时更改您的实现层次,而无需做任何改动这些方法。

    ... then these methods don't care what your object hierarchy looks like, as long as the objects you give them implement the interfaces they ask for. You can change your implementation hierarchy at any time without having to change these methods at all.

    这篇关于只读一流的设计当一个非只读类是已经到位的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆