java阅读笔记
前言:面向对象设计的几大原则?
1》针对接口编程,而不是针对实现编程
2》优先使用对象组合,而不是类继承
1.只根据抽象类中定义的接口来操纵对象有以下两个好处?
1)客户无须知道他们使用对象的特定类型,只需对象有客户所期望的接口
2)客户无须知道他们使用的对象是用什么类来实现的,他们只须知道定义接口的抽象类
这将极大地减少子系统实现之间的相互依赖关系,也产生了可复用的面向对象设计的如下原则:
针对接口编程,而不是针对实现编程
2.继承和组合的比较?
继承:
类继承是在编译时刻静态定义的,且可直接使用,因为程序设计语言支持类继承,可以较方便的改变被复用的实现。
1)继承在编译时刻就定义了,所以无法再运行时刻改变从父类继承的实现,并且父类通常至少定义了部分子类的具体表示。因为继承对子类揭示了其父类的实现细节,常被认为
“破坏了封装性”,子类中的实现与它的父类有如此紧密的依赖关系,以至于父类实现中的任何变化必然导致子类发生变化。
2)如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换,这种依赖关系限制了灵活性并最终限制复用性。一个方法:只继承抽象类(提供较少的实现)
组合:
1)对象组合是通过获得对其他对象的引用而在运行时刻动态定义的。组合要求对象遵守彼此的接口约定,进而要求更仔细的定义接口,而这些接口并不会妨碍你将一个对象和其他对象
一起使用。对象只能通过接口来访问,并不破坏封装性;只要类型一致,运行时刻还可以用一个对象来替代另一个对象;更进一步,因为对象的实现是基于接口编写的,所以很少依赖
2)优先使用对象组合有助于你保持每个类被封装,并被集中在单个任务上。这样的类和类继承层次会保持较小的规模,另一方面,基于对象组合的设计会有更多的对象(而有较少的类)
且系统的行为将依赖于对象间的关系而不是被定义在某个类中
导出我们的面向对象设计的第二个原则:
优先使用对象组合,而不是类继承
3.模式与框架的区分?
1)设计模式比框架更抽象
框架能够用代码表示,而设计模式只有其实例才能表示为代码。框架的威力在于它们能够使用程序设计语言写出来,它们不仅能被学习,也能被直接执行和复用
2)设计模式是比框架更小的体系结构元素
一个典型的框架包含了多个设计模式,而反之绝非如此
3)框架比设计模式更加特例化
框架总是针对一个特定的应用领域。一个图形编辑器框架可能被用于一个工厂模拟,但它不会被认错为是一个模拟框架
框架变得越来越普遍和重要。它们是面向休息系统获得最大复用的方式。较大的面对对象将会由多层彼此合作的框架组成。应用的大部分设计和代码将来自于他所使用的框架
第二章Java程序设计中最基本的设计模式(23种)
设计模式有什么:描述一个设计模式,通常包含如下四个部分:
模式名称:就是为每一个设计模式取个名字,好记忆交流
坏境和问题:描述在什么场景下,出现什么样的特定的问题
解决方案:描述如何解决问题
效果:描述模式可能带来的问题,或者使用过程中需要权衡的问题
一、单例模式
问题:采用什么方法来控制创建类实例,然后确保在任何给定的时间只创建一个类实例
方案:Singleton模式主要作用是保证在Java程序中,一个类只有一个实例存在。很多时候,比如建立目录,数据库连接都需要这样的单线程操作。
Singleton能够被状态化,这样,多个单态类在一起就可以作为一个状态仓库一样向外提供服务 例子:论坛中的帖子计数器--synchronize的安全自动加一
Singleton也能够被无状态化。好处在于可以节省内存,因为它限制了实例的个数,有利于Java垃圾回收
使用示例:一般Singleton模式通常有两种形式:
one:较安全些
public class Singleton{
private Singleton(){}
//在自己内部定义自己的一个实例,是不是很奇怪?
//注意这是private只供内部调用
private static Singleton instance = new Singleton();
//这里提供了一个供外部访问本class的静态方法,可以直接访问
public static Singleton getInsatnce(){
return instance;
}
}
two:
public class Singleton{
//第一次调用初始Singleton,以后就不用再生成了
private static Singleton instance = null;
public static synchronized Singleton getInstance(){
//这个方法比上面的有所改进,不用每次都进行生成对象,只是第一次
//使用时产生实例,提高了效率
if(instance==null)
instance = new Singleton();
return instance;
}
}
使用Singleton注意事项:(真正使用好,需要对Java的类线程内存等概念有相当的了解)
1.有时在某些情况下,使用Singleton并不能达到效果,在有多个Singleton对象同时被不同的类装入器装载;在EJB这样的分布式系统中使用也要注意这种情况,
因为EJB是跨服务器的,跨JVM的
分析说明:在EJB中,Singleton模式会失去作用,SUN公司的宠物店源码中ServiceLocator才分为两种,一种是面向WEB的,一种是面向EJB服务的
二、工厂模式---标准的创建对象的方法
问题:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂模式使一个类的实例化延迟到其子类
方案:工厂模式是一种创建性模式,它定义了一个创建对象的接口,但是却让子类来决定具体实例化哪一个类。当一个类无法预料要创建哪一个类对象或是一个类
需要由子类来指定创建的对象时我们就需要用到工厂模式了。简单来说工厂模式可以根据不同的条件产生不同的实例(相同的类型),工厂模式把这些实例的
具体过程封装起来了,简化了客户端的应用,也改善了程序的扩展性,使得将来可以做最小的改动就可以加入新的待创建的类
为何如此常用?
因为工厂模式就相当于创建实例对象的new,我们经常要根据类class生成实例对象,如A a = new A()工厂模式也是用来创建实例对象的,所以以后new时要多个心眼
是否可以考虑使用工厂模式,虽然这样做,可能多做一些工作,但是会给你的系统带来更大的可扩展性和尽量少的修改量
案例:
我们以类Sample为例,我们要创建Sample的实例对象:Sample sample = new Sample();可是实际情况是通常我们都要在创建sample实例时做点初始化的工作,比如:
赋值,查询数据库等
首先想到的就是使用Sample的构造函数,这样生成实例就写成:Sample sample = new Sample(参数);创建实例化时所作的初始化工作不像赋值那么简单,可能是很长
的代码写入构造函数中,那么你的代码将会很难看(为何?)-----初始化工作如果是很长的一段代码,说明要做的工作很多,将很多工作装入一个方法中,
相当于将很多的鸡蛋放在一个篮子里,是很危险的,这也有悖于Java面向对象的原则,面向对象的封装和分派告诉我们,尽量将长的代码分派切割成每段,
将每段再封装起来(减少段与段之间耦合联系性),以后如果需要修改,只要更改每段,不会再发生牵一动百的事情
工厂模式中有:工厂方法(Factory Method),抽象工厂(Abstract Factory),这两个模式区别在于需要创建对象的复杂程度上
@1.工厂方法:
建立一个专门生产Sample实例的工厂:
public class Factory{
public static Sample creator(int which){
//getClass产生Sample 一般可使用动态类装载入类
if(which==1)
return new SampleA();
else if(which==2)
return new SampleB();
}
}
那么在你的程序中,如果要实例化Sample,就使用 Sample sampleA = Factory.creator(1);
这样在整个涉及到Sample的具体子类,达到封装的效果,也就减少错误修改的机会 通俗来讲:即具体的事情做得越多,越容易犯错
使用工厂方法要注意几个角色,首先你要定义产品接口,如上面的Sample,产品接口下有接口的实现类,如SampleA,其次要有一个factory类,用来生成产品Sample
进一步稍微复杂点,就是在工厂类上进行扩展,工厂类也有继承它的实现类concreateFactory了
@2.抽象工厂
如果我们创建对象的方法变得复杂了,如上面工厂方法中是创建一个对象Sample,如果我们还有新的产品接口Sample2,假设Sample和Sample2均有两个concrete类SampleA(B)
那么,我们就将上例中的Factory扩展成抽象工厂:
public abstract class Factory{
public abstract Sample creator();
public abstract Sample2 creator(String name);
}
public class SimpleFactoy extends Factory{
public Sample creator(){
....................
return new SampleA();
}
public Sample2 creator(String name){
....................
return new Sample2A();
}
}
public class BombFactory extends Factory{
public Sample creator(){
..........
return new SampleB();
}
public Sample2 creator(String name){
....................
return new Sample2B();
}
}
从上面看到两个工厂各自生产出一套Sample和Sample2,也许你会产生疑问,为什么我不可以使用两个工厂方法来分别生产Sample和Sample2?
抽象工厂还有另外一个关键要点,是因为SimpleFactory内,生产Sample和生产Sample2的方法之间有一定联系,所以才要将这两个方法捆绑在一个类中。
@三、值对象模式--成为不同层或不同模块之间数据交换的标准方法,体现的是数据的封装,也有利于对象的复用。
问题:应用程序客户端需要与企业Bean之间交换数据,程序间交换数据---基于客户需要与EJB之间大量滴交换数据的情况,具体说来,在J2EE平台中,应用程序通常将服务器端
的程序组件实现为会话bean和实体bean,而这些组件的部分方法则需要将数据返回给客户;这种情况下,通常一个用户会重复调用相关方法多次,直到它得到相关信息
注意的是:多数情况这些方法的调用目的是为了取得单一的信息,例如用户名或者用户地址等
显而易见,在J2EE平台上,这种调用基本上都是远程的,也就是说,用户多次调用相应的方法会给Web带来极大的负担,即使用户和EJB容器加载相同的JVM、OS和计算机
运行EJB程序,由于方法调用被缺省地认为是远程任务,所以这种问题依然存在
方案:值对象(value object)模式通过减少分布式通信的消息而促进数据的交换,通常这里说指的通信是在web层和EJB层之间。在一个远程调用中,一个单一值对象可以被用来
取出一系列相关数据并提供给客户。
当EJB使用值对象的时候,用户可以通过仅仅一次方法调用来取得整个对象,而不是使用多次方法调用以得到对象中每个域的数值;由于值对象是通过值传递来交送给用户
的,所以对于该值的调用或取值都是本地调用,而不是远程方法调用。需注意:这个值对象必须对应于每个属性的访问方法,或者将所有属性都设为公共的
例子:
假设某名为project的业务对象被模拟或者实现为一个实体bean,当客户端调用值对象的getProjectData()方法时,该project实体bean需要通过该值对象向客户端发送数据
public class ProjectVO implements java.io.Serializable{
private String projectId;
private String projectName;
private String managerId;
private String customerId;
private Data startDate;
private Date endDate;
private boolean started;
private boolean completed;
private boolean accepted;
................
//Value object constructors
以下就是具体的每一个get和set方法
}
@四、DAO模式
问题;目前大部分的J2EE应用程序都需要在一定程度上使用可持久的数据,而实现持久性数据的方法因应用程序不同而异,且访问不同存储格式数据的应用程序接口也有显著的差别
当程序组件,即实体bean、会话bean或servlet、jsp等需要访问数据源时,它们会使用正确的应用程序接口来得到连接并管理数据源,但这样也会造成这些组件与数据程序源
之间的依赖关系,从而使得应用程序很难从一个数据存储实体移植到另一个数据存储实体中去;当数据源的物理实现变化的时候,应用程序也相应地加以改变
如何对存储层以外的模块屏蔽这些复杂性,以提供统一的调用存储实现?
方案:
(1)业务对象:表示数据的用户,它需要对于数据的访问,一个业务对象可以用会话bean、实体bean或是其他Java程序来实现
(2)数据访问对象:是这种模式的主题,它提供了底层数据访问的对象,并将其提供给业务对象以使得后者能够透明地访问数据源;同时业务对象也将数据的加载和存储
操作移交给数据访问对象处理。
(3)数据源:这里指的是数据源的物理实现,这个数据源可以是一个数据库,包括关系型数据库、面向对象数据库或文件系统
(4)值对象:指的是数据载体,数据访问对象可以使用值对象来向用户返回数据,而数据访问对象同样可以从用户那里得到值对象来对数据源中的数据进行更新
应用实例:
抽象的DAOFactory类
public abstract class DAOFactory{
public static final int CLOUDSCAPE=1;
public static final int ORACLE = 2;
public static final int SYBASE = 3;
public abstract CustomerDAO getCustomerDAO();
public abstract AccountDAO getAccountAO();
public abstract OrderDAO getOrderDAO();
....................
public static DAOFactory getDAOFactory(int whichFactory){
switch(whichFactory){
case CLOUDSCAPE:
return new CloudscapeDAOFactory();
case ORACLE:
return new OracleDAOFactory();
case SYBASE:
return new SybaseDAOFactory();
......
default:
return null;
}
}
}
具体的DAOFactory的实现:
public class CloudscapeDAOFactory extends DAOFactory{
public abstract CustomerDAO getCustomerDAO(){
return new CloudscapeCustomerDAO();
}
public abstract AccountDAO getAccountAO(){
return new CloudscapeAccountDAO();
}
public abstract OrderDAO getOrderDAO(){
return new CloudscapeOrderDAO();
}
}
4.3:Customer的基本DAO接口
public interface CustomerDAO{
public int insertCustomer(.....);
public boolean deleteCustomer(....);
public Customer findCustomer(....);
........
}
4.4.Customer的Cloudscape DAO实现
public class CloudscapeCustomerDAO implements CustomerDAO{
public CloudscapeCustomerDAO(){初始化;}
public int insertCustomer(.....){....};
public boolean deleteCustomer(....){.....};
public Customer findCustomer(....){......};
}
4.5:Customer值对象
public class Customer implements java.io.Serializable{
int CustomerNumber;
Stirng name;
String streetAddress;
String city;
.......//getter and setter methods
}
4.6:使用DAO和DAO工厂-----客户端代码
//创建一个DAO工厂
DAOFactory cloudscapeFactory = DAOFactory.getDAOFactory(DAOFactory.DAOCLOUDSCAPE);
//创建一个DAO
CustomerDAO custDAO = cloudscapeFactory.getCustomerDAO();
//创建一个用户
int newCustNo = custDAO.insertCustomer(.....);
//查找到一个用户
Customer cust = custDAO.findCustomer(.....);
//设置参数
cust.setAddress(...);
cust.setEmial(...);
//更新
custDAO.updateCustomer(cust);
..................等等其他操作
第三章 Java程序设计和模式应用
前面我们讲到了两个面向对象的设计原则:
一是针对接口编程,而不是针对实现编程
二是优先使用对象组合,而不是类继承
下面我们讲学习其他的常见的面向对象的设计原则:
1.开放-封闭法则(OCP)即对新增开放,对修改封闭。而且应尽量做到不用修改模块的源代码,就能更改模块的行为。
认为我们应该设计出永远也不需要改变的模块,我们可以添加新代码来扩展系统的行为。我们不能对已有的代码进行修改
符合OCP的模块需满足两个标准:
1)可扩展,即“对扩展是开放的”,---模块的行为可以被扩展,以满足新的需求
2)不可更改,即“对更改是封闭的”----模块的源代码是不允许进行改动的
如何去做呢?
1)抽象 2)多态 3)继承 4)接口
一个软件系统的所有模块不可能都满足OCP,但是我们应该努力最小化这些不满足OCP的模块数量
开放-封闭法则是OO设计的真正核心
符合该法则便意味着最高等级的复用性和可维护性
2.依赖性倒置原则:
就是依赖抽象而不要依赖具体的实现
3.接口隔离原则:
就是不要使用通用的接口,而是为不同的用户使用不同的接口
4.替换原则:
即子类应当可以替换父类并出现在父类能够出现的任何地方
类设计的基本经验:
类要单一
加强内聚,松散耦合
好的封装性
类的粒度要合理
实现类不能依赖它的使用类
应考虑灵活性,也就是可配置、可维护
要考虑性能,考虑可伸缩性
要考虑今后可能的变化,也就是可扩展性
要考虑合理的复用
要合理的考虑接口和抽象类的使用
尽量减少类与协作类的交互次数和交互信息的量
父类不应知道子类的信息,子类必须知道父类的信息
更多的使用类的组合,而不是继承
访问对象必须通过接口,不能绕过接口直接访问
分层:最典型的三层架构,表现层--》逻辑层--》数据层
表现层功能:展示数据、人机交互、收集参数调用逻辑层
逻辑层功能:进行数据的逻辑校验、进行逻辑判断、实现业务功能、处理相关功能、处理后续流程、组织数据返回给表现层
数据层功能:实现数据持久化、实现对象和持久化数据的双向映射
层间交互的基本原则:
1.表现层调用逻辑层,逻辑层调用数据层,不可反过来!!
2.层间交互也应该通过接口进行调用,以确保各层的实现独立变化