Java语言布局性模式之变压器模式先容
副标题#e#
什么是布局性模式
布局性模式描写类和工具奈何团结在一起成为较大的布局。 布局性模式描写两种差异的对象:类与类的实例。按照它们所描写的对象的差异, 布局性模式可以分为类布局模式和实例布局模式两种。
类布局模式利用担任(inheritance)来把类,接口等组合在一起,形成更大的布局。 当一个类从父类担任,并实现某接口时,这个新的类就把父类的布局和接口的布局团结起来。 类布局模式是静态的。一个类布局模式的典范的例子,就是类形式的变压器模式。
实例布局模式描写各类差异范例的把工具组合在一起,实现新的成果的要领。实例布局模式是动态的。 一个典范的实例布局模式,就是署理人模式,署理人模式将在今后先容。其它的例子包罗后头将要先容的复合模式, 航行重量模式,装饰模式,以及实例形式的变压器模式等。
有一些模式会有类布局模式的形式和实例布局模式的形式两种,成为以上两种形式的布局模式的极好注解。 本节要先容的变压器模式就是这样,它有类形式和实例形式两种。
变压器模式的先容
变压器模式把一个类的接口调动成客户端所等候的另一种接口。变压器模式使原本无法在一起事情的两个类可以或许在一起事情。 如前所述,变压器模式是关于类布局的布局性模式,因而是静态的模式。
这很象变压器(Adapter)—变压器把一种电压调动成另一种电压。当我把美国的电器拿回中国大陆去用的时候, 我就面对电压差异的问题。美国的糊口用电压是110伏,而中国的电压是220伏。我假如要在中国大陆利用我在美国利用的电器, 我就必需有一个能把220伏电压转换成110伏电压的变压器。而这正象是本模式所做的事,因此此模式被称为变压器模式。
读者大概也会想到,Adapter在中文也可翻译为转换器(适配器)。实际上,转换器(适配器)也是一个符合的名字。仍用电器作例子, 美国的电器的插头一般是三相的,即除了阳极,阴极外,尚有一个地极。中国大陆的修建物内的电源插座一般只有南北极,没有地极。 这时候,即便电器简直可以接管220伏电压,电源插座和插头不匹配,也使电器无法利用。 一个三相到两相的转换器(适配器)就能办理这个问题。因此此模式也可被称为转换器(适配器)模式。
同时,这种做法也很象包装进程,被包装的物体的真实样子被包装所掩盖和改变,因此有人把这种模式叫做包装(Wrapper)模式。事实上, 我们常常写许多这样的wrapper类,把已有的一些类包裹起来,使之能有满意需要的接口。
变压器模式有类形式和实例形式两种差异的形式。
类形式的变压器模式的界说
类形式的变压器模式的类图界说如下。
图1. 类形式的类变压器模式的类图界说
在图1可以看出,模式所涉及的成员有:
方针(Target)。这就是我们所等候获得的接口。留意,由于这里接头的是类变压器模式,因此方针不行以是类。
源(Adaptee)。现有需要适配的接口。
变压器(Adapter)。变压器类是本模式的焦点。变压器把源接口转换成方针接口。显然,这一脚色不行以是接口, 而必需是实类。
#p#副标题#e#
本模式的示范代码如下:
package com.javapatterns.adapter.classAdapter;
public interface Target
{
/**
* Class Adaptee contains operation sampleOperation1.
*/
void sampleOperation1();
/**
* Class Adaptee doesn't contain operation sampleOperation2.
*/
void sampleOperation2();
}
代码清单1. Target的源代码。
package com.javapatterns.adapter.classAdapter;
public class Adaptee
{
public void sampleOperation1(){}
}
代码清单2. Adaptee的源代码。
package com.javapatterns.adapter.classAdapter;
public class Adapter extends Adaptee implements Target
{
/**
* Class Adaptee doesn't contain operation sampleOperation2.
*/
public void sampleOperation2()
{
// Write your code here
}
}
代码清单3. Adapter的源代码。
类形式的变压器模式的结果
第一、利用一个实类把源(Adaptee)适配到方针(Target)。这样一来,假如你想把源以及源的子类都利用此类适配, 就行不通了。
第二、由于变压器类是源的子类,因此可以在变压器类中置换(override)掉源的一些要领。
第三、由于只引进了一个变压器类,因此只有一个蹊径达到方针类。问题获得简化。
实例形式的变压器模式的界说
实例形式的变压器模式的类图界说如下。
图2. 实例变压器模式的类图界说
在图1可以看出,模式所涉及的成员有:
方针(Target)。这就是我们所等候获得的接口。方针可以是实的或抽象的类。
源(Adaptee)。现有需要适配的接口。
变压器(Adapter)。变压器类是本模式的焦点。变压器把源接口转换成方针接口。 显然,这一脚色必需是实类。
本模式的示范代码如下:
#p#分页标题#e#
package com.javapatterns.adapter;
public interface Target {
/**
* Class Adaptee contains operation sampleOperation1.
*/
void sampleOperation1();
/**
* Class Adaptee doesn't contain operation sampleOperation2.
*/
void sampleOperation2();
}
代码清单4. Target的源代码。
package com.javapatterns.adapter;
public class Adapter implements Target {
public Adapter(Adaptee adaptee){
super();
this.adaptee = adaptee;
}
public void sampleOperation1(){
adaptee.sampleOperation1();
}
public void sampleOperation2(){
// Write your code here
}
private Adaptee adaptee;
}
代码清单5. Adapter的源代码。
package com.javapatterns.adapter;
public class Adaptee {
public void sampleOperation1(){}
}
代码清单6. Adaptee的源代码。
实例形式的变压器模式的结果
第一、一个变压器可以把多种差异的源适配到同一个方针。换言之,同一个变压器可以把源类和它的子类都适配到方针接口。
第二、与类形式的变压器模式对比,要想置换源类的要领就不容易。假如必然要置换掉源类的一个或多个要领,就只好先做一个源类的子类, 将源类的要领置换掉,然后再把源类的子类看成真正的源举办适配。
第三、固然要想置换源类的要领不容易,可是要想增加一些新的要领例利便得很。 并且新增加的要领同时合用于所有的源。
在什么环境下利用变压器模式
在以下各类环境下利用变压器模式:
第一、你需要利用现有的类,而此类的接口不切合你的需要。
第二、你想要成立一个可以反复利用的类,用以与一些互相之间没有太大关联的一些类, 包罗一些大概在未来引进的类一起事情。这些源类不必然有很巨大的接口。
第三、(对实例形式的变压器模式而言)你需要改变多个已有的子类的接口, 假如利用类形式的变压器模式,就要针对每一个子类做一个变压器类,而这不太实际。
J2SE中的变压器模式的利用
在Java语言2.0的尺度SDK中,有许多的变压器类。如:
库措施包java\awt\event中有
ComponentAdapter ContainerAdapter FocusAdapter HierarchyBoundsAdapter KeyAdapter MouseAdapter MouseMotionAdapter WindowAdapter
库措施包Javax\swing\event中有
InternalFrameAdapter MouseInputAdapter
这些都是变压器模式利用的实际例子。值得指出的是,WindowAdapter的成立者们不行能预见到你所要利用的方针接口, 因此WindowAdapter不行能实现你的方针接口。可是,在考查了这些变压器类的利用范畴之后,我们会发明, WindowAdapter只需实现WindowListener的接口即可,也就是说,方针接口被省略了。请见下面的表明。
抽象类WindowAdapter是变压器模式的一个例子
抽象类WindowAdapter是为接管视窗的事件而筹备的。此抽象类内所有的要领都是空的。 利用此类可以很利便地创建listener工具。置换(Override)你所感乐趣的谁人事件所对应的要领。 假如你不利用此抽象类,那么你一定纪律要实现WindowsListener接口,而那样你就不得不实现所有接口中的要领, 即即是你不需要的事件所对应的要领,你也要给出一个空的要领,而这显然不利便。
显然,抽象类WindowAdapter的方针接口可以选得与源接口一样,而不影响结果。 这就表明白为什么方针接口不呈此刻WindowAdapter类图(见下面)里。
图3. 本例子SwingUI类与WindowAdapter实例变压器模式的类图界说
SwingUI类的代码如下。
import java.awt.Color;
import java.awt.BorderLayout;
import java.awt.event.*;
import javax.swing.*;
class SwingUI extends JFrame implements ActionListener
{
JLabel text, clicked;
JButton button, clickButton;
JPanel panel;
private boolean m_clickMeMode = true;
Public SwingUI()
{
text = new JLabel("我很兴奋!");
button = new JButton("理我");
button.addActionListener(this);
panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.setBackground(Color.white);
getContentPane().add(panel);
panel.add(BorderLayout.CENTER, text);
panel.add(BorderLayout.SOUTH, button);
}
public void actionPerformed(ActionEvent event)
{
Object source = event.getSource();
if (m_clickMeMode)
{
text.setText("我很烦!");
button.setText("别理我");
m_clickMeMode = false;
}
else
{
text.setText("我很兴奋!");
button.setText("理我");
m_clickMeMode = true;
}
}
public static void main(String[] args)
{
SwingUI frame = new SwingUI();
frame.setTitle("我");
WindowListener listener = new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
};
frame.addWindowListener(listener);
frame.pack();
frame.setVisible(true);
}
}
代码清单7. SwingUI类的源代码。赤色的代码就是利用WindowAdapter的无名内部类。
显然,由于无名内部类是担任自WindowAdapter抽象类,因此只需置换(override)掉我们需要的要领, 即windowClosing()而不必劳神WindowListener的其它要领。
本例子在运行时的样子:
图4. SwingUI类在运行时的样子。单击呼吁键“理我”就酿成下图的样子。
图5. 再单击呼吁键“别理我”就会回到前图的样子。
操作变压器模式指方为圆
#p#分页标题#e#
中国古代有赵高指鹿为马的故事。鹿与马有许多相似之处,没见过的人本就判别不清,指一指大概没什么大不了的。 指方为圆是否过分?非也。本例就是要指方为圆,需要的只是变压器模式这个把戏手指(Magic Finger)。
变压器模式在本例子的类图如下。
图6. 指方为圆的变压器模式类图
package com.javapatterns.adapter.cube2ball;
public class Cube
{
public Cube(double width)
{
this.width = width;
}
public double calculateVolume()
{
return width * width * width;
}
public double calculateFaceArea()
{
return width * width;
}
public double getWidth()
{
return this.width;
}
public void setWidth(double width)
{
this.width = width;
}
private double width;
}
代码清单8. Cube类的源代码。。
package com.javapatterns.adapter.cube2ball;
public interface BallIF
{
double calculateArea();
double calculateVolume();
double getRadius();
void setRadius(double radius);
}
代码清单9. BallIF接口的源代码。
package com.javapatterns.adapter.cube2ball;
public class MagicFinger implements BallIF
{
public MagicFinger(Cube adaptee)
{
super();
this.adaptee = adaptee;
radius = adaptee.getWidth();
}
public double calculateArea()
{
return PI * 4.0D * ( radius * radius );
}
public double calculateVolume()
{
return PI * 4.0D/3.0D * ( radius * radius * radius );
}
public double getRadius()
{
return radius;
}
public void setRadius(double radius)
{
this.radius = radius;
}
private double radius = 0;
private static final double PI = 3.14D;
private Cube adaptee;
}
代码清单10. MagicFinger类的源代码。
#p#分页标题#e#
假如读者还记得中学的数学的话,应该可以看出,我们的指方为圆系统其实照旧有原理的。它接管一个正方体, 返还此正方体的内切球,也就是能放进此正方体的最大的球。
显然,本例子里,我们利用的是实例形式的变压器模式。这样做的长处是,假如一旦我们抉择不只要支持正方体, 并且要支持四面体等多面体,我们可以利用同一个MagicFinger类,而不必针对每一个多面体都成立一个MagicFinger类。 这样也较量切合“把戏手指”这个名字。
关于模式实现的接头
本模式在实现时有以下这些值得留意的处所:
第一、方针接口可以省略。此时,方针接口和源接话柄际上是沟通的。 由于源是一个接口,而变压器类是一个类(或抽象类),因此这种做法看似平庸而并不服庸, 它可以使客户端不必实现不需要的要领。这一点已经在WindowAdapter的例子里做了详尽的阐明。
第二、变压器类可以是抽象类。这已经在WindowAdapter的例子里看到了。实际上,WindowAdapter的例子过于简朴。 实际的景象里,你大概想给出一些实要领。
第三、带参数的变压器模式。利用这种步伐,变压器类就不必,有时大概不能是源类的子类。 变压器类按照参数返还一个符合的实例给客户端。
问答题
第1题、请做一个小猫(kittie)的实类,并实现miao(),catchRat(),run(),sleep()等要领。 再做一个小狗(doggie)的接口,要求有wao(),fetchBall(),run(),sleep()等要领。
此刻你的女伴侣想要一只小狗,但是你只找到的一只小猫。请用变压器模式把小猫“适配成”小狗, 让你的女伴侣满足。(提示:量力而为。)
第2题、请指出第一题的解答所利用的是那一种形式的变压器模式。
第3题、笔者在很多场所给各类差异水准的专业人士作过各类编程模式的先容,发明介入OOP开拓事情的差异时间是非的人, 对差异的模式领略接管的速度有所差异。唯独在讲过这个男伴侣与小狗小猫的例子后,各人对变压器模式的领略都很精确。 让笔者百思不得其解。你知道这是奈何回事吗?
第4题、请讲一讲利用实例形式的变压器模式和利用类形式的变压器模式在第一题的办理上有何影响。
问答题谜底
第1题、按照提示,我们可以量力而为。因此,我们将把miao()“适配成”wao(),catchRat()“适配成”fetchBall(), run(),sleep()稳定。源代码如下:
图7. 男伴侣小狗适配器的类图。
package com.javapatterns.adapter.kittie2doggie;
public interface Doggie
{
void wao();
void fetchBall();
void run();
void sleep();
void setName(String name);
String getName();
}
代码清单11. SwingUI类的源代码。赤色的代码就是利用WindowAdapter的无名内部类。
package com.javapatterns.adapter.kittie2doggie;
public class Kittie {
public void miao(){}
public void catchRat() {
}
public void run() {
}
public void sleep() {
}
public String getName(){ return name; }
public void setName(String name){ this.name = name; }
}
代码清单12. SwingUI类的源代码。赤色的代码就是利用WindowAdapter的无名内部类。
package com.javapatterns.adapter.kittie2doggie;
public class Boyfriend extends Kittie implements Doggie
{
public void wao()
{
this.miao();
}
public void fetchBall()
{
this.catchRat();
}
public void run()
{
super.run();
}
public void sleep()
{
super.sleep();
}
public String getName()
{
return super.getName();
}
public void setName(String name)
{
super.setName(name);
}
}
代码清单13. SwingUI类的源代码。赤色的代码就是利用WindowAdapter的无名内部类。
怎么,她不满足呀?那也有步伐:把wao(),fatchBall()看成新的要领,在变压器类中实现。由于你饰演变压器脚色, 当她挪用wao(),fatchBall()要领是,你就叫一声,或把球捡返来就可以了。
你不满足呀?那就再去找一只真正的小狗吧。变压器模式的威力就到此为止了。
第2题、这里利用的是类形式的变压器模式。
第3题、我的一个学生汇报我,领略这个问题的要害,即男伴侣必需装小狗。
#p#分页标题#e#
第4题、利用类形式的功效是,她一旦想要另一个宠物,她就得换一个男伴侣。 利用实例形式的变压器模式的功效是,她假如想要另一个宠物,本来的男伴侣就得身兼几种身份。