我来自C#背景,现在已经做了很长时间的编程了。但直到最近我才开始对我如何编程提出一些看法。显然,我的OOP非常糟糕。
我有几个问题也许有人可以帮助我。它们是基本的,但我想证实。
1-在C#中,我们可以声明类属性,如
private int _test;
并且有像
这样的setter getterspublic int Test {get; set;}
现在,让我说我必须在类中使用此属性。我会用哪一个?私人或公众?或者它们都是一样的?
2-让我们说我必须实现一个执行XML解析的类。我们可以使用不同的东西作为类的输入,如“FILE PATH”。我应该将它作为一个类PROPERTY还是我应该将它作为参数传递给类中的公共函数?哪种方法更好。请检查以下
我可以创建一个类属性并像这样使用
public string FilePath {get; set;}
public int Parse()
{
var document = XDocument.Load(this.FilePath);
.........//Remaining code
}
或者
我可以将文件路径作为参数传递
public int Parse(string filePath)
我应该在什么基础上决定我应该制作一个属性,或者我应该传递一些东西作为论据?
我知道这些问题的解决方案,但我想知道正确的方法。如果你可以推荐一些视频讲座或书籍也会很好。
答案 0 :(得分:2)
好像你有一些术语混淆了。
private int _test;
这是实例字段(也称为成员)。 该字段允许直接访问类内的值。
请注意,我说“在课堂内”。因为它是private
,所以无法从课外访问它。这对于保留封装非常重要,这是OOP的基石。封装基本上告诉我们实例成员不能直接在类外部访问。
出于这个原因,我们创建了成员private
,并提供了“设置”和“获取”变量的方法(至少:在Java中就是这样)。这些方法暴露给外界,强迫任何使用你的类的人通过你的方法而不是直接访问你的变量。
应该注意的是,当你在当前类中时,你也想要使用你的方法/属性。每次不这样做,您都有可能绕过验证规则。安全地玩,并始终使用方法而不是支持字段。
netto的结果是你可以强制你的逻辑应用于更改(设置)或检索(获取)。最好的例子是验证:通过强制人们使用您的方法,您的验证逻辑将在(可能)将字段设置为新值之前应用。
public int Test {get; set;}
这是自动实施的属性。粗略地说一个属性是一种使用get / set方法的简单方法。
在幕后,您的代码会转换为
private int _somevariableyoudontknow;
public void setTest(int t){
this._somevariableyoudontknow = t;
}
public int getTest(){
return this._somevariableyoudontknow;
}
所以对于吸气者和制定者来说真的非常相似。关于属性的好处在于,您可以在一行中定义您在7行中执行的操作,同时仍然保留显式getter和setter的所有可能性。
我的验证逻辑在哪里,你问? 要添加验证逻辑,您必须创建自定义实施属性。
语法如下:
private int _iChoseThisName;
public int Test {
get {
return _iChoseThisName;
}
set {
if(value > 5) { return _iChoseThisName; }
throw new ArgumentException("Value must be over 5!");
}
}
基本上我们所做的就是为您的get
和set
提供实施方案。请注意value
关键字!
属性可以这样使用:
var result = SomeClass.Test; // returns the value from the 'Test' property
SomeClass.Test = 10; // sets the value of the 'Test' property
最后一点注意事项:仅仅因为您拥有名为Test
的属性,并不意味着支持变量名为test
或_test
。编译器将为您生成一个变量名,以一种永远不会重复的方式作为后备字段。
如果您希望得到第二个答案,那么您将必须展示当前架构的外观。
虽然它应该没有必要:将它作为构造函数的参数传递是最有意义的。您应该为要解析的每个文件创建一个新的XmlParser
(随机名称)对象。解析后,您不想更改文件位置。
如果你想要这样做:创建一个进行解析的方法,让它将文件名作为参数,这样你仍然可以将它保留在一个调用中。
您不想创建属性,原因很简单,您可能忘记设置属性并调用解析方法。
答案 1 :(得分:1)
我可以回答你的第一个问题。你问“我必须在课堂上使用这个属性。”听起来像你需要使用私有变量。您提供的公共方法我认为只会做两件事:允许客户端设置您的一个私有变量,或允许客户端“查看”(获取)私有变量。但是如果你想“在类中使用这个属性”,那么在处理类中的数据时,私有变量应该是你的焦点。节日快乐:)
答案 2 :(得分:1)
第一个问题确实包含两个问题。
1)我是否应该使用getter和setter(Accessors和Mutators)来访问成员变量。
答案取决于变量的实现是否可能发生变化。在某些情况下,接口类型(getter返回的类型,并由setter设置)需要保持一致,但存储数据的基础机制可能会发生变化。例如,属性的类型可以是String,但实际上数据存储在更大的String的一部分中,getter提取String的那部分并将其返回给用户。
2)我应该给予财产什么样的可见性?
可见性完全取决于使用。如果该属性需要可供其他类或从基类继承的类访问,则该属性需要是公共的或受保护的。
我从未将实施暴露给外部问题。这就是说我总是在公共和受保护的数据上放置一个getter和setter,因为它有助于我确保即使底层实现发生变化,我也会保持接口相同。外部变化的另一个常见问题是,我希望有机会拦截外部用户修改属性的尝试,可能是为了防止它,但更有可能使对象处于良好或安全状态。这对于可能作为属性公开的缓存值尤其重要。考虑一个对值数组的内容求和的属性。您不希望每次引用时重新计算该值,因此您需要确定数组中元素的setter告诉对象需要重新计算总和。这样可以使计算保持最小。
我认为第二个问题是:我何时创建一个可以传递给构造函数公开的值?
取决于值的用途。我通常认为有两种不同类型的变量传递给构造函数。有助于创建对象的那些(您的XML文件路径就是一个很好的例子)和那些传入的对象,因为对象将负责管理。例如,在集合中,您通常可以使用数组初始化集合。
我遵循这些准则。
如果传入的值可以在不损坏对象状态的情况下进行更改,则可以将其设置为属性并公开显示。
如果更改传入的值会损坏对象的状态或重新定义其标识,则应将其留给构造函数初始化状态,并且不能通过属性方法再次访问。
由于OO Design中有许多不同的范例和语言,很多这些术语都令人困惑。了解OO设计中良好实践的最佳地点是从一本关于模式的好书开始。虽然所谓的四人帮http://en.wikipedia.org/wiki/Design_Patterns多年来一直是标准,但自那以后出版了许多更好的书。
以下是设计模式的几个资源:
http://sourcemaking.com/design_patterns
还有一对C#具体。
http://msdn.microsoft.com/en-us/magazine/cc301852.aspx
http://www.codeproject.com/Articles/572738/Building-an-application-using-design-patterns-and
答案 3 :(得分:1)
以下是我个人的观点,基于我在各种编程语言中的个人经验。我不认为最佳实践对于所有项目都不一定是静态的。
何时使用getter,何时直接使用私有实例变量
取决于。
你可能知道这一点,但让我们来谈谈为什么我们通常需要getter和setter而不是公共实例变量:它允许我们获得OOP的全部功能。
虽然实例变量只是一些转储内存(哑巴的数量肯定取决于你正在使用的语言),但是getter并没有绑定到特定的内存位置。 getter允许OOP hirarchy中的子项覆盖“实例变量”的行为而不受其约束。因此,如果你有interface
有各种实现,有些可能使用ab实例变量,而有些可能使用IO从网络获取数据,从其他值计算,等等。
因此,getter不一定返回实例变量(在某些语言中这更复杂,例如带有virtual
关键字的c ++,但我会尝试在这里与语言无关。)
为什么这与内部类行为有关?如果你有一个带有非final getter的类,getter和inner变量可能会返回不同的值。因此,如果您需要确定它是内部值,请直接使用它。但是,如果您依赖于“真实”值,请始终使用getter。
如果getter是final或者语言强制getter相等(这种情况比第一种情况更常见),我个人更喜欢直接访问私有字段;这使得代码易于阅读(imho)并且不会产生任何性能损失(不适用于所有语言)。
何时使用参数,何时使用实例变量/属性
尽可能使用参数。
绝不要将实例变量或属性用作参数。方法应尽可能独立。在您所述的示例中,参数化版本更好用imo。
Intance变量(带有getter或不带getter)是实例的属性。因为它们是实例的一部分,所以它们应该在逻辑上与它绑定。
看看你的例子。如果您听到XMLParser
这个词,您对此有何看法?你认为解析器只能解析它绑定的单个文件吗?或者你认为解析器可以解析任何文件?我倾向于最后一个(另外,使用实例变量会另外杀死线程安全)。
另一个例子:您希望创建一个XMLArchiver
,将多个xml文档放入一个存档中。在实现时,您将文件名作为构造函数的参数,可能会打开文件的输出流并将对它的引用存储为实例变量。然后,您多次拨打archiver.add(stuff-to-add)
。如您所见,文件(因此,文件名)自然地绑定到XMLArchiver
实例,而不是添加文件的方法。