结构型模式
主要是对类的结构和类与类之间的关系进行的设计,采用类间或者接口与类间的关联组合或依赖来实现为对象添加新的功能。
续前2篇
装饰者模式
目标:
通过类之间关联的方式,动态地给一个对象添加额外的功能和职责。(可以理解为在已有的函数功能上再添加上额外的职责)
why-思考:
一般对一个对象添加额外的功能,采用继承的方式可以解决,但是也会因为继承的方式把父类的其他方法功能也继承下来,这不是我们扩展对象的目的。怎么通过类之间的关联方式继承来实现尼并且做到不会像继承一样带来副作用尼?
how-思考:
分清角色,需要添加功能的对象(被装饰者)和实现要添加功能的对象(装饰者)。使用对象之间的关联方式,在原来的基础上添加额外的功能。
JDK中的示例:
BufferedInputStream(装饰者)、InputerStream(被装饰者)
//1.定义你需要动态添加功能的对象的类——被装饰者
public abstract class InputStream extends Object implements Closeable {
......//其他函数
//1.1需要增加功能的函数部分
public int read(byte[] buffer, int offset, int length) throws IOException {
//从输入流中读取字节并存入到字节数组buffer中
Arrays.checkOffsetAndCount(buffer.length, offset, length);
for (int i = 0; i < length; i++) {
int c;
try {
if ((c = read()) == -1) {
return i == 0 ? -1 : i;
}
} catch (IOException e) {
if (i != 0) {
return i;
}
throw e;
}
buffer[offset + i] = (byte) c;
}
return length;
}
//2.定义实现要添加功能的对象的类——装饰者
//2.1抽象装饰者FilterInputStream,需要将被装饰者关联。
FilterInputStream extends InputStream {
protected volatile InputStream in;
protected FilterInputStream(InputStream in) {
this.in = in;
}
}
//3.实现动态添加功能——具体装饰者
public class BufferedInputStream extends FilterInputStream{
public BufferedInputStream(InputStream in, int size) {
super(in);
}
//3.1在原来的read基础上,实现添加额外功能
@Override
public synchronized int read(byte[] buffer, int offset, int byteCount) throws IOException {
......
InputStream localIn = in;
while (true) {
int read;
if (markpos == -1 && required >= localBuf.length) {
//3.2原来的read函数,其他部分便是动态添加的代码功能
read = localIn.read(buffer, offset, required);
if (read == -1) {
return required == byteCount ? -1 : byteCount - required;
}
}
......
}
//4.最后实现,为InputStream对象添加了BufferedInputStream 中的功能
InputStream bufferInput=new BufferedInputStream (new InputStream(),1024)
适配器模式
目标:
将一个类的接口变换成客户端所期待的另外一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作.
why-思考:
在软件系统中,如何将一些已有的现存对象放到新的不同接口的环境中还能继续工作尼?
how-思考:
想办法将两种接口相结合,一方面能用到原来的对象和方法,可以将适配者依赖原来的对象,另一方面需要适应新的环境,需要实现新的接口方法。
JDK中的示例:
适配者InputStreamReader,新的环境Reader
//1.要适应的新环境Reader字符流
public abstract class Reader implements Readable, Closeable {
......
public abstract int read(char[] buf, int offset, int count) throws IOException;
......
}
//2.现有的对象和方法
public abstract class InputStream extends Object implements Closeable {
......//其他函数
public int read(byte[] buffer, int offset, int length) throws IOException {
......//省略的代码
}
}
//3.定义适配者InputStreamReader,去适应新环境
public class InputStreamReader extends Reader{
//3.2需要将旧环境中的对象给关联进来
private InputStream in;
public InputStreamReader(InputStream in, final String charsetName){
.....
this.in=in;
......
}
@Override
public int read(char[] buffer, int offset, int length) throws IOException {
//3.3将现有改成适应新的环境的接口
.....
int actualByteCount = in.read(bytes.array(), off, desiredByteCount);
.....
}
}
代理模式
目标:
为其他对象提供一种代理以控制对这个对象的访问
why-思考:
如何对访问一个类的对象时加以控制?
how-思考:
在访问者和被访问者间加上中间层。
JDK中的示例:
分为静态代理和动态代理
//--------------------静态代理,JDK中暂时没有找到
//1.定义对象对外提供的代理访问方法的接口
public interface IXXDao{
public void dothing();
}
//2.被代理的对象类,也是目标对象
public XXDao implements IXXDao{
public void dothing(){
......//do something
}
}
//3.代理对象,中间件
public XXProxy implements IXXDao{
//3.1 代理对象,需要关联目标对象
IXXDao dao;
public void setIXXDao(IXXDao dao){
this.dao=dao;
}
//3.2 具体的代理操作
public void dothing(){
......//控制对dao的访问的代码
dao.dothing();
}
}
//--------------------动态代理,JDK中java.lang.reflect.Proxy
//说明
//1.代理对象,不需要实现接口,通过JDK中的API动态生成代理对象,但是需要我们指定创建代理对象和目标对象实现的接口的类型。
//2.目标对象(被代理的对象一定要实现接口。
//已经定义好目标对象dao(XXDao)
//使用Proxy.newProxyInstance创建代理对象
IXXDao proxy=(IXXDao)Proxy.newProxyInstance(dao.getClass().getClassLoader(),dao.getClass().getInterfaces(),new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
System.out.println("do before......");
Object object=method.invoke(dao, args);
System.out.println("do after......");
return object;
}
});
//代理实现
proxy.dothing();
//newProxyInstance中参数说明:
//1. 类加载器(Class Loader)目标对象的类的加载器
//2. 需要实现的接口数组,(目标对象实现的接口数组)
//3. InvocationHandler接口。所有动态代理类的方法调用,都会交由InvocationHandler接口实现类里的invoke()方法去处理。
组合模式
目标:
将对象组合成树形结构以表示“部分和整体”的层次结构,使用户对单个对象和组合对象的使用性保持具有一致性。
why-思考:
如何构建一个树形结构?怎么使得单个对象和组合对象使用方法保持一致?
how-思考:
使用类之间的组合关系。树枝和叶子实现统一接口,树枝内部组合该接口。
JDK中的示例:
javax.swing.JComponent
//1.定义整体和部分共同的特征并抽象为一个类——
public abstract class Component implements ImageObserver, MenuContainer, Serializable
{
public void setFont(Font f) {
Component.this.setFont(f);
}
}
//2.具体的组合对象
public class Container extends Component {
private List<Component>component=new ArrayList<Component>();
public void setFont(Font f) {
boolean shouldinvalidate = false;
Font oldfont = getFont();
super.setFont(f);
Font newfont = getFont();
if (newfont != oldfont && (oldfont ==null ||!oldfont.equals(newfont))) {
// 将component每一个都setFont
invalidateTree();
}
}
//3.单个对象,最终祖类还是Component
public class JLabel extends JComponent{
.....
//依然可以调用setFont函数,只不过在祖类中实现了
}
感想
1.主要总结了结构型中常用的几种模式。
2.学会合理的使用类之间的组合和依赖有时候比类之间的继承效果来的更好。
3.类之间的组合和依赖,尽量要解耦,不要依赖或者关联具体的类。