Java设计模式——结构型
本章接着讨论结构型的设计模式。
6、适配器模式
该模式将一个类的接口转换成用户希望的另外接口,目的是消除由于接口不匹配所造成的类的兼容性问题。
——优点:
- 目标和被适配者是完全解耦的关系
- 模式满足开闭原则,当添加一个实现Adaptee接口的新类时,不必修改Adapter,Adapter就能对这个新类的实例进行适配
——适用场景:
- 程序想使用已存在的类,但该类所实现的接口和当前程序所使用的接口不一致
——应用实例:
- 集合包中java.util.Arrays#asList()、io包中java.io.InputStreamReader(InputStream)、java.io.OutputStreamWriter(OutputStream)
6.1 对象的适配器模式
被适配者——已存在的类
public class Adaptee {
public void method1(){
System.out.println("this is original method!");
}
}
目标接口——客户想要的
public interface Targetable {
public void method1();
public void method2();
}
public class Adapter implements Targetable{
private Adaptee adaptee; //持有Adaptee类的实例
public Adapter(Adaptee adaptee) {
this.adaptee=adaptee;
}
@Override
public void method1() {
adaptee.method1();
}
@Override
public void method2() {
System.out.println("this is the targetable method!");
}
}
public class AdapterTest {
public static void main(String[] args) {
Targetable target=new Adapter(new Adaptee());
target.method1();
target.method2();
}
}
6.2 接口的适配器模式
接口的适配器是这样的:一个接口有多个抽象方法,有时并不是所有的方法都是我们需要的,为解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。
public interface Adapteeable {
public void method1();
public void method2();
}
public abstract class Wrapper implements Adapteeable{
public void method1(){}
public void method2(){}
}
public class AdapteeSub extends Wrapper{
/*重写*/
public void method1(){
System.out.println("this is original method!");
}
}
public class AdapterTest {
public static void main(String[] args) {
AdapteeSub sub=new AdapteeSub();
sub.method1(); //有输出
sub.method2(); //没输出
}
}
7、装饰模式
装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
——优点:
- 被装饰者和装饰者是松耦合关系
- 满足开闭原则,不必修改具体组件,就可以增加新的针对该具体组件的具体装饰
——适用情景:
- 动态增强类的某个对象的功能,而又不影响该类的其他对象
- 采用继承来增强对象功能不利于系统的扩展和维护
——应用实例:
- Java IO对象,如继承了InputStream和OutputStream的IO类,FileInputStream(InputStream)、BufferedInputStream(InputStream)等
抽象构件
public interface Component {
public void method();
}
具体构件
public class ConcreteComponent implements Component{
@Override
public void method() {
System.out.println("原方法");
}
}
装饰器
public class Decorator implements Component{
private Component component;
public Decorator(Component component) {
this.component=component;
}
@Override
public void method() {
component.method();
}
}
具体装饰器
public class ConcreteDecorator extends Decorator{
public ConcreteDecorator(Component component) {
super(component);
}
public void method(){
super.method();
System.out.println("装饰方法");
}
}
public class DecoratorTest {
public static void main(String[] args) {
Component component=new ConcreteComponent();
Decorator decorator=new ConcreteDecorator(component);
decorator.method();
}
}
8、代理模式
为其他对象提供一种代理以控制对这种对象的访问。
——优点:
- 代理模式可屏蔽用户真正请求的对象,使用户和真正的对象之间解耦
——适用情景:
- 程序不希望用户直接访问该对象,而是提供一个特殊的对象以控制对当前对象的访问
- 如果一个对象(如大图像)需要很长时间才能加载完成
- 如果对象位于远程主机上,需要为用户提供访问该远程对象的能力
与装饰器模式的区别:代理模式体现的是透明调用思想,代理类的构造方式是一个无参构造函数,而装饰器模式一般需要将被装饰对象作为参数来进行显示地构造,以体现装饰器的思想
抽象主题
public interface Sourceable {
public void method();
}
具体主题
public class Source implements Sourceable{
@Override
public void method() {
System.out.println("the original method!");
}
}
代理类
public class Proxy implements Sourceable{
private Sourceable source;
public Proxy() {
source=new Source();
}
@Override
public void method() {
source.method();
System.out.println("代理方法");
}
}
public class ProxyTest {
public static void main(String[] args) {
Sourceable source=new Proxy();
source.method();
}
}
9、外观模式
外观模式是为了解决类与类之家的依赖关系的,像spring一样,可以将类和类之间的关系配置到配置文件中,而外观模式就是将他们的关系放在一个Facade类中,降低了类类之间的耦合度,该模式中没有涉及到接口。
——优点:
- 使用户与子系统中的类无耦合,并使子系统使用起来更方便
——使用情景:
- 对于一个复杂的子系统,需要为用户提供一个简单的交互操作
public class CPU {
public void startup(){
System.out.println("cpu startup!");
}
public void shutdown(){
System.out.println("cpu shutdown!");
}
}
public class Memory {
public void startup(){
System.out.println("memory startup!");
}
public void shutdown(){
System.out.println("memory shutdown!");
}
}
public class Disk {
public void startup(){
System.out.println("disk startup!");
}
public void shutdown(){
System.out.println("disk shutdown!");
}
}
public class Computer {
private CPU cpu;
private Memory memory;
private Disk disk;
public Computer(){
cpu = new CPU();
memory = new Memory();
disk = new Disk();
}
public void startup(){
cpu.startup();
memory.startup();
disk.startup();
}
public void shutdown(){
cpu.shutdown();
memory.shutdown();
disk.shutdown();
}
}
public class User {
public static void main(String[] args) {
Computer computer = new Computer();
computer.startup();
computer.shutdown();
}
}
10、桥接模式
桥接模式:将抽象化与实现化解耦,使得两者可以独立变化。
——使用情景:
- 不想让抽象和某些重要的实现代码是固定的绑定关系,这部分实现可运行时动态决定
- 如果希望类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充;如果不使用桥接模式,使用继承去实现时,在抽象类中添加一个方法,则在对应的实现类中也需要做对应的改动,这种实现不符合松耦合的要求
——使用实例:
- JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了
public interface Sourceable {
public void method();
}
public class SourceSub1 implements Sourceable{
@Override
public void method() {
System.out.println("this is the first sub!");
}
}
public class SourceSub2 implements Sourceable{
@Override
public void method() {
System.out.println("this is the second sub!");
}
}
public abstract class Bridge {
private Sourceable source;
public void method(){
source.method();
}
public Sourceable getSource(){
return source;
}
public void setSource(Sourceable source){
this.source=source;
}
}
public class MyBridge extends Bridge{
public void method(){
getSource().method();
}
}
public class BridgeTest {
public static void main(String[] args) {
Bridge bridge=new MyBridge();
Sourceable source1=new SourceSub1();
bridge.setSource(source1);
bridge.method();
Sourceable source2=new SourceSub2();
bridge.setSource(source2);
bridge.method();
}
}
11、亨元模式
享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。
——使用情景:
- 一个应用程序使用大量的对象,这些对象间部分属性本质上是相同的,这时应使用亨元来封装相同的部分。
- 对象状态可变为外部状态,就可以考虑将这样对象作为系统中的亨元来使用
——使用实例:
- 包装类的valueOf()方法,如Integer.valueOf()
public interface Flyweight {
public void operation();
}
public class ConcreteFlyweight implements Flyweight{
private String str;
public ConcreteFlyweight(String str) {
this.str=str;
}
@Override
public void operation() {
System.out.println(str);
}
}
public class Factory {
private HashMap<String, Flyweight> map=new HashMap<>();
public Flyweight getFlyWeight(String str){
Flyweight fw=map.get(str);
if(fw==null){
fw=new ConcreteFlyweight(str);
map.put(str, fw);
}
return fw;
}
public int size(){
return map.size();
}
}
public class FlyweightTest {
private static Factory f=new Factory();
static{
f.getFlyWeight("A");
f.getFlyWeight("B");
f.getFlyWeight("C");
f.getFlyWeight("D");
}
public static void main(String[] args) {
System.out.println(f.size()); //4
Flyweight f1=f.getFlyWeight("G");
System.out.println(f.size()); //5
Flyweight f2=f.getFlyWeight("B");
System.out.println(f.size()); //5
f2.operation(); //B
}
}