一篇关于OO的论述,感觉写的很有道理。依照作者的观点静态函数最好不要用,而它应该是它的参数的方法。比如求-3的绝对值,使用Math.abs(int i)静态方法,但是最好是-3.abs(),这样,数据和方法就会耦合在一起,否则,这将是纯粹的,面向过程。而且,某个对象的方法若是和一个输入参数(该参数为一个自定义对象时)耦合很紧密,并且比该方法所在对象还紧密时,应该重构,将该方法放入该参数对象当中。
Everyone seems to think that they are writing OO after all they are using OO languages
such as Java, Python or Ruby. But if you exam the code it is often procedural in nature.
StaticMethods
Static methods are procedural in nature and they have no place in OO world. I can
already hear the screams, so let me explain why, but first we need to agree that global
variables and state is evil. If you agree with previous statement than for a static method to
do something interesting it needs to have some arguments, otherwise it will always return
a constant. Call to a staticMethod() must always return the same thing, if there is no
global state. (Time and random, has global state, so that does not count and object
instantiation may have different instance but the object graph will be wired the same
way.)
This means that for a static method to do something interesting it needs to have
arguments. But in that case I will argue that the method simply belongs on one of its
arguments. Example: Math.abs(-3) should really be -3.abs(). Now that does not imply
that -3 needs to be object, only that the compiler needs to do the magic on my behalf,
which BTW, Ruby got right. If you have multiple arguments you should choose the
argument with which method interacts the most.
But most justifications for static methods argue that they are “utility methods”. Let’s say
that you want to have toCamelCase() method to convert string “my_workspace” to
“myWorkspace”. Most developers will solve this as
StringUtil.toCamelCase(“my_workspace”). But, again, I am going to argue that the
method simply belongs to the String class and should be “my_workspace”.toCamelCase().
But we can’t extend the String class in Java, so we are stuck, but in many other OO
languages you can add methods to existing classes.
In the end I am sometimes (handful of times per year) forced to write static methods due
to limitation of the language. But that is a rare event sincestatic methods are death to
testability. What I do find, is that in most projects static methods are rampant.
InstanceMethods
So you got rid of all of your static methods but your codes still is procedural. OO says that
code and data live together. So when one looks at code one can judge how OO it is
without understanding what the code does, simply by looking at the relationship of data
and code.
class Database {
// some fields declared here
boolean isDirty(Cache cache, Object obj) {
for (Object cachedObj : cache.getObjects) {
if (cachedObj.equals(obj))
return false;
}
return true;
}
}
The problem here is the method may as well be static! It is in the wrong place, and you
can tell this because it does not interact with any of the data in the Database, instead it
interacts with the data in cache which it fetches by calling the getObjects() method. My
guess is that this method belongs to one of its arguments most likely Cache. If you move it
to Cache you well notice that the Cache will no longer need the getObjects() method since
the for loop can access the internal state of the Cache directly. Hey, we simplified the code
(moved one method, deleted one method) and we have made Demeter happy.
The funny thing about the getter methods is that it usually means that the code where the
data is processed is outside of the class which has the data. In other words the code and
data are not together.
class Authenticator {
Ldap ldap;
Cookie login(User user) {
if (user.isSuperUser()) {
if ( ldap.auth(user.getUser(),
user.getPassword()) )
return new Cookie(user.getActingAsUser());
} else (user.isAgent) {
return new Cookie(user.getActingAsUser());
} else {
if ( ldap.auth(user.getUser(),
user.getPassword()) )
return new Cookie(user.getUser());
}
return null;
}
}
Now I don’t know if this code is well written or not, but I do know that the login() method
has a very high affinity to user. It interacts with the user a lot more than it interacts with
its own state. Except it does not interact with user, it uses it as a dumb storage for data.
Again, code lives with data is being violated. I believe that the method should be on the
object with which it interacts the most, in this case on User. So lets have a look:
class User {
String user;
String password;
boolean isAgent;
boolean isSuperUser;
String actingAsUser;
Cookie login(Ldap ldap) {
if (isSuperUser) {
if ( ldap.auth(user, password) )
return new Cookie(actingAsUser);
} else (isAgent) {
return new Cookie(actingAsUser);
} else {
if ( ldap.auth(user, password) )
return new Cookie(user);
}
return null;
}
}
Ok we are making progress, notice how the need for all of the getters has disappeared,
(and in this simplified example the need for the Authenticator class disappears) but there
is still something wrong. The ifs branch on internal state of the object. My guess is that
this code-base is riddled with if (user.isSuperUser()). The issue is that if you add a new
flag you have to remember to change all of the ifs which are dispersed all over the
code-base. Whenever I see If or switch on a flag I can almost always know that
polymorphism is in order.
class User {
String user;
String password;
Cookie login(Ldap ldap) {
if ( ldap.auth(user, password) )
return new Cookie(user);
return null;
}
}
class SuperUser extends User {
String actingAsUser;
Cookie login(Ldap ldap) {
if ( ldap.auth(user, password) )
return new Cookie(actingAsUser);
return null;
}
}
class AgentUser extends User {
String actingAsUser;
Cookie login(Ldap ldap) {
return new Cookie(actingAsUser);
}
}
Now that we took advantage of polymorphism, each different kind of user knows how to
log in and we can easily add new kind of user type to the system. Also notice how the user
no longer has all of the flag fields which were controlling the ifs to give the user different
behavior. The ifs and flags have disappeared.
Now this begs the question: should the User know about the Ldap? There are actually two
questions in there. 1) should User have a field reference to Ldap? and 2) should User have
compile time dependency on Ldap?
Should User have a field reference to Ldap? The answer is no, because you may want to
serialize the user to database but you don’t want to serialize the Ldap. See here.
Should User have compile time dependency on Ldap? This is more complicated, but in general the
answer depends on wether or not you are planning on reusing the User on a different project, since
compile time dependencies are transitive in strongly typed languages. My experience is that
everyone always writes code that one day they will reuse it, but that day never comes, and when it
does, usually the code is entangled in other ways anyway, so code reuse after the fact just does not
happen. (developing a library is different since code reuse is an explicit goal.) My point is that a
lot of people pay the price of “what if” but never get any benefit out of it. Therefore don’t worry
abut it and make the User depend on Ldap.
分享到:
相关推荐
《How to Think about Algorithms》是一本算法设计的经典教材,不仅适用于初学者入门,对于那些希望进一步提高算法设计能力的进阶读者也同样适用。该书在算法理论与实践之间架起了一座桥梁,教会读者如何从思维层面...
How to Think About Algorithms 英文epub 本资源转载自网络,如有侵权,请联系上传者或csdn删除 查看此书详细信息请在美国亚马逊官网搜索此书
Understand how to formulate problems, think creatively about solutions, and write programs clearly and accurately Determine which development techniques work best for you, and practice the important ...
根据文件提供的信息,这本书的名字是《How to Think Like a Computer Scientist: Learning with Python Documentation Release 2nd Edition》,是一本面向读者的Python学习教材。本书的作者是Jeffrey Elkner, Allen ...
Learn How to Think Critically, Build Quality Relationships, Take Interesting Classes
Think Perl 6: How to Think Like a Computer Scientist by Laurent Rosenfeld English | 8 May 2017 | ASIN: B0716P9W11 | 466 Pages | AZW3 | 1.02 MB Want to learn how to program and think like a computer ...
Garr Reynolds演讲PPT(部分中文注释) how to think like a designer.part2.rar
### 如何像计算机科学家一样思考:从教学实践中学习Python的重要性 #### 教学背景与语言选择挑战 在1999年,美国大学理事会(College Board)决定将高级编程(AP Computer Science)考试的语言从传统的Pascal改为...
How to Think Like a Computer Scientist-Learning with Python
其中推荐的PPT《How to think like a designer》由Garr Reynolds创作,为我们揭示了如何培养设计师的思维方式,这对于任何想要提升创新能力和解决问题能力的人来说都是宝贵的资源。 首先,我们要理解设计师思考的...
《Think Python》是一本关于Python编程语言和计算机科学基础的教程。由Allen B. Downey所著,本书最初是作为教授Java入门课程的辅助材料编写,但随后被改写为以Python语言为中心的编程书籍。本书在不同时间经历了多...
本书是针对没有经验或没有经验的人的计算机科学和编程的简介。 它从最基本的概念开始,并在首次使用时仔细定义所有术语。
“What actually drove me to a therapist was that I had a very unhealthy obsession with laundry.” 19 ca r in goldberg “When I was growing up, designers were anonymous. They didn’t do schtick....
是一本用java来描述数据结构,算法的书。 很适合初学者
面向初学者的Java编程简介。 它是为准备参加计算机科学高级进阶(AP)考试的学生量身定制的,但它是为想要学习Java的任何人而设计的。
Garr Reynolds演讲PPT(部分中文注释) how to think like a designer.part1.rar