Java初学者入门经典:面向对象的思维方法(其实同样非常适合C#的初学者)……非原创,作者忘了,非常抱歉...

 

我是从学习Java编程开始接触OOP(面向对象编程),刚开始使用Java编写程序的时候感觉很别扭,因为我早以习惯用C来编写程序,很欣赏C的简洁性和高效性,喜欢C简练而表达能力丰富的风格,特别忍受不了Java运行起来慢吞吞的速度,相对冗长的代码,而且一个很简单的事情,要写好多类,一个类调用一个类,心里的抵触情绪很强。

我对Java的面向对象的特性琢磨良久,自认为有所领悟,也开始有意识的运用OOP风格来写程序,然而还是经常会觉得不知道应该怎样提炼类,面对一个具体的问题的时候,会觉得脑子里千头万绪的,不知道怎么下手,一不小心,又会回到原来的思路上去。

举个例子,要发广告邮件,广告邮件列表存在数据库里面。倘若用C来写的话,一般会这样思考,先把邮件内容读入,然后连接数据库,循环取邮件地址,调用本机的qmailsendmail命令发送。

然后考虑用Java来实现,既然是OOP,就不能什么代码都塞到main过程里面,于是就设计了三个类:

一个类是负责读取数据库,取邮件地址,调用qmailsendmail命令发送;

一个类是读邮件内容,MIME编码成HTML格式的,再加上邮件头;

一个主类负责从命令读参数,处理命令行参数,调用发email的类。

把一件工作按照功能划分为3个模块分别处理,每个类完成一件模块任务。

仔细的分析一下,就会发现这样的设计完全是从程序员实现程序功能的角度来设计的,或者说,设计类的时候,是自低向上的,从机器的角度到现实世界的角度来分析问题的。因此在设计的时候,就已经把程序编程实现的细节都考虑进去了,企图从底层实现程序这样的出发点来达到满足现实世界的软件需求的目标。

这样的分析方法其实是不适用于Java这样面向对象的编程语言,因为,如果改用C语言,封装两个C函数,都会比Java实现起来轻松的多,逻辑上也清楚的多。

我觉得面向对象的精髓在于考虑问题的思路是从现实世界的人类思维习惯出发的,只要领会了这一点,就领会了面向对象的思维方法。

举一个非常简单的例子:假使现在需要写一个网页计数器,客户访问一次页面,网页计数器加1,计数器是这样来访问的http://hostname/count.cgi?id=xxx

后台有一个数据库表,保存每个id(一个id对应一个被统计访问次数的页面)的计数器当前值,请求页面一次,对应id的计数器的字段加1(这里我们忽略并发更新数据库表,出现的表锁定的问题)

如果按照一般从程序实现的角度来分析,我们会这样考虑:首先是从HTTP GET请求取到id,然后按照id查数据库表,获得某id对应的访问计数值,然后加1,更新数据库,最后向页面显示访问计数。

现在假设一个没有程序设计经验的人,他会怎样来思考这个问题的呢?他会提出什么样的需求呢?他很可能会这样想:

我需要有一个计数器,这个计数器应该有这样的功能,刷新一次页面,访问量就会加1,另外最好还有一个计数器清0的功能,当然计数器如果有一个可以设为任意值的功能的话,我就可以作弊了。

做为一个没有程序设计经验的人来说,他完全不会想到对数据库应该如何操作,对于HTTP变量该如何传递,他考虑问题的角度就是我有什么需求,我的业务逻辑是什么,软件应该有什么功能。

按照这样的思路(请注意,他的思路其实就是我们平时在生活中习惯的思维方式),我们知道需要有一个计数器类 Counter,有一个必须的和两个可选的方法:

getCount()

// 取计数器值方法

resetCounter()

// 计数器清0方法

setCount()

// 设计数器为相应的值方法

 

Counter类完整的定义如下:

 

public class Counter

{

public int getCount(int id)

{

}

public void resetCounter(int id)

{

}

public void setCount

(int id, int currentCount)

{

}

}



解决问题的框架已经有了,来看一下如何使用Counter。在count.cgi里面调用Counter来计数,程序片断如下:

// 这里从HTTP环境里面取id

...

Counter myCounter = new Counter();

// 获得计数器

int currentCount = myCounter.getCount(id);

// 从计数器中取计数

// 这里向客户浏览器输出

...



程序的框架全都写好了,剩下的就是实现Counter类方法里面具体的代码了,此时才去考虑具体的程序语言实现的细节,比如,在getCount()方法里面访问数据库,更新计数值。

从上面的例子中看到,面向对象的思维方法其实就是我们在现实生活中习惯的思维方式,是从人类考虑问题的角度出发,把人类解决问题的思维方式逐步翻译成程序能够理解的思维方式的过程,在这个翻译的过程中,软件也就逐步被设计好了。

在运用面向对象的思维方法进行软件设计的过程中,最容易犯的错误就是开始分析的时候,就想到了程序代码实现的细节,因此封装的类完全是基于程序实现逻辑,而不是基于解决问题的业务逻辑。

学习JDBC编程的经典错误问法是:我怎样封装对数据库的select操作?

面向对象的设计是基于解决业务问题的设计,而不是基于具体编程技术的设计。我不会去封装select语句的,我只封装解决问题的业务逻辑,对数据库的读取是在业务逻辑的编码实现阶段才去考虑的问题。

回过头看上面那个发广告邮件的例子,应该如何应用面向对象的思维方法呢?

对于一个邮件来说,有邮件头,邮件体,和邮件地址这三个属性,发送邮件,需要一个发送的方法,另外还需要一个能把所有邮件地址列出来的方法。所以应该如下设计:

JunkMail

属性:

head

body

address



方法:

sendMail()

// 发送邮件

listAllMail()

// 列邮件地址



Java来表示:

public class JunkMail

{

private String head;

private String body;

private String address;

public JunkMain()

{

// 默认的类构造器

// 从外部配置文件读邮件头和邮件体

this.head=...;

this.body=...;

}

 

public static boolean

sendMail(String address)

{

// 调用qmail,发送email

}

 

public static Collection listAllMail()

{

// 访问数据库,返回一个邮件地址集合

}

}



当把JunkMail设计好了以后,再调用JunkMail类完成邮件的发送,将是非常轻松的事情。

如果说传统的面向过程的编程是符合机器运行指令的流程的话,那么面向对象的思维方法就是符合现实生活中人类解决问题的思维过程。

在面向对象的软件分析和设计的时候,要提醒自己,不要一上来就去想程序代码的实现,应该抛开具体编程语言的束缚,集中精力分析我们要实现的软件的业务逻辑,分析软件的业务流程,思考应该如何去描述和实现软件的业务。毕竟软件只是一个载体,业务才是我们真正要实现的目标。

但是在设计过程中,心里却往往在担心,如果我完全不去考虑程序代码的实现的话,那么我怎么知道我的设计一定合理呢?我怎么知道我设计的类、接口一定可以实现呢?所以经常可以看到的现象就是:

在设计过程中,虽然知道不能过早考虑代码实现,但是每设计一个类,一个接口,心里都要不知不觉的用自己熟悉的编程语言大概的评估一下,看看能否编出来,因此,一不小心,就会又回到按照程序功能实现的思路进行设计的老路上去了。

举个例子来说明,在做Web程序设计的时候,经常要遇到分页显示数据的情况。比如说需要把系统中所有的用户都列出来这样的功能。假设使用User类来表示用户,增加用户addUser(),删除用户deleteUser(),查询所有用户listUsers()方法。而数据库中有一个user表,一条记录是一个用户的信息。下面考虑一下User类的方法的实现:

addUser()
deleteUser()方法都好实现,就是对数据库增加记录和删除记录。对于listUsers()方法,其实就是对user表的select,取出一个记录集。但是该怎么从listUsers()方法中得到所有用户的列表呢?

一个方法调用的返回值只有一个,没有多个,所以很多情况下采用的办法就是返回值定义为集合类型,比如Vector。这样就可以在listUsers()方法的具体代码实现的时候,从数据库依次取出一个个记录,插入到Vector里面来。在主程序里面,调用listUsers()方法可以返回一个Vector,然后再对Vector遍历操作,就可以得到用户列表了。

public class User

{

 

public static void addUser(...)

{

// 数据库insert一条记录

}

 

public static void deleteUser(...)

{

// 数据库delete一条记录

}

 

public Vector listUsers(...)

{

// 数据库select结果放到一个集合里面

}

}



这样的设计基本合理,但是仍然有点小问题。因为在设计的时候,就考虑到了用Java的集合类Vector来实现对不定长数据集的存放,因而违反了面向对象设计的一个原则:在设计的时候不应过早的考虑具体程序语言的实现。所以必须用抽象的方法,和具体实现无关的方法来表达业务逻辑。

我们知道,通常对具有集合特征的数据结构进行遍历通常可以使用nexthasNext方法,next实现取下一个用户,hasNext判断是否还有元素。 因此我们定义一个接口Iterator,这个接口中定义两个方法nexthasNext

public interface Iterator

{

public boolean hasNext()

{

}

public Object next()

{

}

}

 

User类的listUses方法返回值

改为Iterator接口的实现类:

 

public class User

{

...

public Iterator listUsers()

{

}

...

}



这样就把User类的设计和具体的实现方法分离开了,因为此时任何实现了next()hasNext()方法的类都可以做为listUsers的返回值,都可以被用来表达用户列表,而不仅仅可以使用Vector而已。

比如,我可以用ArrayList来表达用户列表,因为ArrayList也实现了Iterator,当然我也可以自己专门写一个类来存放用户列表,只要实现next()hasNext()方法就行了。

这样在具体的编写代码的时候,程序员具有了最大的灵活性,可以根据具体的情况,采用不同的编程方法来存放用户列表。特别是降低了程序的耦合度,提高了程序的可移植性。对于上面那个JunkMaillistAllMail()方法也同样应该改为接口类型。

然后,在主程序里面就这样来使用User类的listUsers方法:

User myUser = new User();

Iterator iterator = myUser.listUsers();

while (iterator.hasNext())

{

iterator.next();

}



这样就可以完全不用考虑程序代码实现了,从高层次上把功能抽象出来,定义成为接口,同时又可以把系统设计的很合理,完全根据业务的需求来进行设计。

结语

通过上面的几个例子的设计说明,使用面向对象的思维方法,其实是一个把业务逻辑从具体的编程技术当中抽象出来的过程,而这个抽象的过程是自上而下的,非常符合人类的思维习惯,也就是先不考虑问题解决的细节,把问题的最主要的方面抽象成为一个简单的框架,集中精力思考如何解决主要矛盾,然后在解决问题的过程中,再把问题的细节分割成一个一个小问题,再专门去解决细节问题。

因而一旦牢牢的抓住了这一点,你就会发现在软件设计和开发过程中,你自己总是会不知不觉的运用面向对象的思维方法来设计和编写程序,并且程序的设计和开发也变得不再那么枯燥,而一个合理运用面向对象技术进行设计和架构的软件,更是具备了思维的艺术美感。最后,愿面向对象的思维方法也能给您的程序设计之路带来创作的乐趣。

转载于:https://www.cnblogs.com/FlyEagle/archive/2005/08/23/220931.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/263433.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

极客

2019独角兽企业重金招聘Python工程师标准>>> 极客是美国俚语“geek”的音译。随着互联网文化的兴起,这个词含有智力超群和努力的语意,又被用于形容对计算机和网络技术有狂热兴趣并投入大量时间钻研的人。现代的Geek含义虽然与过去有所不同&am…

大数据——SparkStreaming学习笔记

Spark 一、SparkStreaming ​ Spark Streaming 用于流式数据的处理(准实时,微序列)。Spark Streaming 支持的数据输入源很多,例如:Kafka、 Flume、Twitter、ZeroMQ 和简单的 TCP 套接字等等。数据输入后可以用 Spark…

[推荐]ORACLE SQL:经典查询练手第三篇(不懂装懂,永世饭桶!)

[推荐]ORACLE SQL:经典查询练手第三篇(不懂装懂,永世饭桶!) [推荐]ORACLE SQL: 经典查询练手第三篇(不懂装懂,永世饭桶!) ——通过知识共享树立个人品牌。 本文与大家共同讨论与分享ORACLE SQL的一些常用经…

ACM试题 - ASCII码排序 - Java中字符与对应ASCII码的转换

Java中字符转换对应ASCII码有两种方式: 第一种: char c a; byte b (byte)c; // b97 第二种: char c a; int b c; // b97 而一个ASCII码转换成相应字符则仅需强制转换: int a 97; char c (char)a; // ca ACM试…

DotNET多线程使用初探

最近几周一直在做DotNET WinForm开发,陆陆续续有些收获,希望能够有空好好整理整理。记下来以免以后又忘了。:-) 一、最简单的线程使用方法 新建一个C# Windows应用程序项目,在最前面的引用代码那增加一行using System.Threading;在界面上扔个…

Linux mount: Structure needs cleaning 错误解决方法

“mount: Structure needs cleaning”报错可以使用xfs_repair来修复,但是要注意 xfs_repair修复的分区中的文件都会丢失,即使是du能正常显示的文件也会丢失。 [rootyw-0-0 /]# mount -a mount: Structure needs cleaning[rootyw-0-0 /]# xfs_repair /dev…

大数据——Spark学习笔记(配置)

Spark运行环境 spark ui web http://hadoop102:8080 历史服务器 http://hadoop102:18080 一. 本地模式(Local)——单机运行 本地模式:运行 bin/spark-shell提交应用:运行 bin/spark-submit --class org.apache.spark.examples.SparkPi …

Linux软件安装——安装软件的命令

Linux软件安装——安装软件的命令 摘要:本文主要学习了如何在Linux系统中安装、更新、卸载软件。 rpm命令 rpm命令用来在Linux系统上进行软件的安装。 基本语法 安装命令: 1 rpm -ivh 包全名 如果没有安装则安装,如果已经安装则升级&#xff…

JavascriptHelp

阅读全文:http://www.cckan.net/forum.php?modviewthread&tid147usingSystem;usingSystem.Data;usingSystem.Configuration;usingSystem.Web;usingSystem.Web.Security;usingSystem.Web.UI;usingSystem.Web.UI.WebControls;usingSystem.Web.UI.WebControls.Web…

GoLang之方法与接口

GoLang之方法与接口 Go语言没有沿袭传统面向对象编程中的诸多概念,比如继承、虚函数、构造函数和析构函数、隐藏的this指针等。 方法 Go 语言中同时有函数和方法。方法就是一个包含了接受者(receiver)的函数,receiver可以是内置类…

孙继海化装

于输得太惨,中国球员怕回国被球迷打,孙继海决定化装成“乞丐”。化好之后遇到一个老太太,他想看看自己化装的效果,就给了老太太100块钱,问:“你知道我是谁吗?”老太太看了看说:“你是…

sql 时间函数

1. 当前系统日期、时间 select getdate() 2. dateadd 在向指定日期加上一段时间的基础上,返回新的 datetime 值 例如:向日期加上2天 select dateadd(day,2,’2004-10-15′) –返回:2004-10-17 00:00:00.000 3. datediff 返回跨两个指定日期的…

大数据——Hive学习笔记

具体代码可以参考: https://github.com/Ostrich5yw/java4BigData

Eclipse 常用快捷键

Eclipse 常用快捷键 快捷键 描述 编辑 Ctrl1 快速修复(最经典的快捷键,就不用多说了,可以解决很多问题,比如import类、try catch包围等) CtrlShiftF 格式化当前代码 CtrlShiftM 添加类的import导入 CtrlShiftO 组织类的…

[Story]狗尾草花园

不知道为什么,很多年过去,我却依然记得这个故事。很小的时候,大概四、五岁吧,在河滩上和几个姐姐,一起玩过家家。他们大概十三,四岁吧,反正比我大好多。分成两组,我在我们一组当然是…

UML各种图

转载自: https://www.cnblogs.com/jiangds/p/6596595.html UML(Unified Modeling Language)是一种统一建模语言,为面向对象开发系统的产品进行说明、可视化、和编制文档的一种标准语言。下面将对UML的九种图包图的基本概念进行介绍…

Interactive Reflection Editing (SIGGRAPH ASIA 09)

讓使用者可以將reflection做editing 以達到使用者想要的結果INPUT: a 3d scene大部分是針對卡通等NPR的場景 對於reflection做editing利用shader也可以控制reflection這篇可以讓使用者做更動沒有使用BRDF做reflection的計算转载于:https://www.cnblogs.com/GameJan/archive/201…

大数据——Hive学习笔记(配置)

具体代码可以参考: https://github.com/Ostrich5yw/java4BigData

CSDN-markdown编辑器语法——字体、字号与颜色

Markdown是一种可以使用普通文本编辑器编写的标记语言,通过类似HTML的标记语法,它可以使普通文本内容具有一定的格式。但是它本身是不支持修改字体、字号与颜色等功能的! CSDN-markdown编辑器是其衍生版本,扩展了Markdown的功能&a…

思科携手中兴掌握3G话语权 剑指华为3COM组合

思科携手中兴掌握3G话语权 剑指华为3COM组合 中国电信市场又迎来了一次中外巨头的牵手。昨日,思科系统公司和中兴通讯股份有限公司共同宣布,双方已签署了一份战略合作协议,将进行范围广泛的合作。这也是林正刚上任思科系统中国总裁后,祭出的首个“中国攻略”。 根据协议,两家公…