结构设计模式展示了如何以灵活和可扩展的方式将系统的不同部分粘合在一起。这些模式特点是保证当其中一个部分发生变化时,整个应用程序结构不需要改变。
适配器设计模式
适配器将一个类的接口转换为另一个类需要的接口。它允许类一起工作,否则由于接口不兼容而无法工作。
适配器设计模式是结构设计模式之一,它用于使两个不相关的接口可以一起工作。连接这些不相关接口的对象称为适配器。
适配器模式也称为包装器模式。当现有系统必须在不修改源代码的情况下采用其他现有组件时,适配器设计对于系统集成非常有用。
适配器设计模式的参与者
参与此模式的类和/或对象如下所列:
目标(BufferedReader):它定义了客户端直接使用的特定于应用程序的接口。
Adapter(InputStreamReader):将接口Adaptee适配到Target接口。是中间人。
Adaptee(System.in):它定义了一个现有的不兼容接口,需要在应用程序中使用之前进行适配。
客户端:这是您的应用程序与 Target 接口一起使用。
另请注意,如果 Target 和 Adaptee 相似,则适配器只需将来自 Target 的请求委托给 Adaptee。如果 Target 和 Adaptee 不相似,则适配器可能必须转换它们之间的数据结构并实现 Target 所需但 Adaptee 未实现的操作。
示例中可能使用的注解解释
@XmlRootElement // xml 文件的根元素
@XmlElement //将java对象的属性映射为xml的节点,在使用@XmlElement时,可通过name属性改变java对象属性在xml中显示的名称。用在get方法上。
@XmlAccessorType // 控制默认情况下是否对字段或 Javabean 属性进行系列化。
@XmlTransient // @XmlTransient 注解解决 JavaBean 属性名称与字段名称之间的名称冲突,或者用于防止字段/属性的映射。阻止将 JavaBean 属性映射到 XML 表示形式
@XmlJavaTypeAdaptor(XmlMapAdapter.class) //指定类型适配器
适配器模式的示例:
java.util.Arrays#asList()
java.io.OutputStreamWriter(OutputStream)
Writer writer = new OutputStreamWriter(new FileOutputStream("c:\\data\\output.txt"));
writer.write("Hello World");
javax.xml.bind.annotation.adapters.XmlAdapter#marshal() 和 #unmarshal()
代码实例
实现实体类序列化
Adaptee.java
package com.company.adapter;
public class Adaptee {
private String key;
private String value;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public Adaptee() {
}
public Adaptee(String key, String value) {
this.key = key;
this.value = value;
}
@Override
public String toString() {
return "Adaptee{" +
"key='" + key + '\'' +
", value='" + value + '\'' +
'}';
}
}
Product.java
package com.company.adapter;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.io.Serializable;
import java.util.Map;
/**
* @XmlRootElement // xml 文件的根元素
* @XmlElement
* @XmlAccessorType // 控制默认情况下是否对字段或 Javabean 属性进行系列化。
* @XmlTransient
* @XmlJavaTypeAdaptor:参考Using JAXB 2.0's XmlJavaTypeAdapter
*/
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Product implements Serializable {
private int id;
@XmlJavaTypeAdapter(XmlMapAdapter.class)
private Map<String,String> map;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public Product() {
}
public Product(int id, Map<String, String> map) {
this.id = id;
this.map = map;
}
@Override
public String toString() {
return "Product{" +
"id=" + id +
", map=" + map +
'}';
}
}
XmlMapAdapter.class
package com.company.adapter;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class XmlMapAdapter extends XmlAdapter<Adaptee[],Map<String,String>> {
@Override
public Map unmarshal(Adaptee[] v) throws Exception {
Map<String,String> map = new HashMap<>();
for(int i=0;i<v.length;i++){
map.put(v[i].getKey(),v[i].getValue());
}
return map;
}
@Override
public Adaptee[] marshal(Map<String,String> v) throws Exception {
Adaptee[] e = new Adaptee[v.size()];
int i = 0;
Set<Map.Entry<String, String>> entrySet = v.entrySet();
for (Map.Entry<String, String> entry : entrySet) {
Adaptee adaptee = new Adaptee();
adaptee.setKey(entry.getKey());
adaptee.setValue(entry.getValue());
e[i++] = adaptee;
}
return e;
}
}
test.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<product>
<id>1</id>
<map>
<item>
<key>称呼</key>
<value>小兵</value>
</item>
<item>
<key>身高</key>
<value>190cm</value>
</item>
<item>
<key>年龄</key>
<value>18</value>
</item>
</map>
</product>
AdapterTest.java
package com.company.adapter;
import javax.xml.bind.JAXB;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class AdapterTest {
public static void main(String[] args) throws IOException {
Product p1 = new Product();
p1.setId(15);
Map<String,String> map = new HashMap<>();
map.put("称呼", "小兵");
map.put("年龄", "18");
p1.setMap(map);
JAXB.marshal(p1,System.out);
Product product = JAXB.unmarshal("E:\\code\\design_pattern\\src\\com\\company\\adapter\\test.xml",Product.class);
System.out.println(product);
}
}
桥设计模式
桥接设计模式用于将一个类解耦为两部分——抽象和它的实现——这样两者都可以在未来发展而不会相互影响。它增加了类抽象与其实现之间的松散耦合。
将抽象与其实现分离,以便两者可以独立变化。
通过从抽象到实现的方法调用之间再添加一个重定向来实现这种解耦。
以下参与者构成了桥梁设计模式。
Abstraction (abstract class)
它定义了抽象接口,即行为部分。它还维护实施者参考。
RefinedAbstraction (normal class)
它扩展了抽象定义的接口。
Implementer (interface)
它定义了实现类的接口。这个接口不需要直接对应抽象接口,可以有很大的不同。抽象 imp 根据实现者接口提供的操作提供了一个实现。
ConcreteImplementor (normal class)
它实现了实施者接口。
更喜欢组合而不是继承。
以相互正交的方式对不同时间进行子类化时,会变得方便。
桥接设计模式最适用于需要提供平台独立性的应用程序。
FileDownloaderAbstracting.java
public interface FileDownloaderAbstracting {
Object download(String path);
boolean store(Object object);
}
RefindedAbstraction.java
public class RefindedAbstraction implements FileDownloaderAbstracting {
private Implementer implementer;
public RefindedAbstraction(Implementer implementer) {
this.implementer = implementer;
}
@Override
public Object download(String path) {
return implementer.downloadFile(path);
}
@Override
public boolean store(Object object) {
return implementer.storeFile(object);
}
}
Implementer.java
public interface Implementer {
Object downloadFile(String path);
boolean storeFile(Object object);
}
LinuxFileDownloadImplementor.java
public class LinuxFileDownloadImplementor implements Implementer{
@Override
public Object downloadFile(String path) {
return new Object();
}
@Override
public boolean storeFile(Object object) {
System.out.println("File downloaded successfully in LINUX !!");
return true;
}
}
WindowsFileDownloadImplementor.java
public class WindowsFileDownloadImplementor implements FileDownloadImplementor
{
@Override
public Object downloadFile(String path) {
return new Object();
}
@Override
public boolean storeFile(Object object) {
System.out.println("File downloaded successfully in WINDOWS !!");
return true;
}
}
Client.java
public class Client {
public static FileDownloaderAbstracting fileDownloaderAbstracting = null;
public static void download(String system){
switch (system){
case "windows":
fileDownloaderAbstracting = new RefindedAbstraction(new WindowsFileDownloadImplementor());
break;
case "linux":
fileDownloaderAbstracting = new RefindedAbstraction(new LinuxFileDownloadImplementor());
break;
}
fileDownloaderAbstracting.download("some path");
fileDownloaderAbstracting.store(new Object());
}
}
TestBridge.java
public class TestBridge {
public static void main(String[] args) {
Client.download("linux");
}
}
输出
File downloaded successfully in LINUX !!
组合设计模式
复合设计模式有助于将对象组合成树结构来表示整个部分的层次结构。可以统一处理带个对象和对象的组合。
- 当应用程序具有分层结构并且需要跨结构的通用功能时。
- 当应用程序需要跨层次聚合数据时。
- 当应用程序想要统一处理复合对象和单个对象时。
复合设计模式是一种修改对象结构的结构模式。这种模式最适合您需要处理形成树状层次结构的对象的情况。在该树中,每个节点/对象(根节点除外)都是复合节点或叶节点。实现复合模式可以让客户统一处理单个对象和组合。
参与此模式的类和对象是:
Component 组件
声明组合中对象的接口。
酌情为所有类通用的接口实现默认行为。
声明一个用于访问和管理其子组件的接口。
Leaf 叶子
表示组合中的叶对象。一片叶子没有孩子。
定义组合中原始对象的行为。
合成的
定义有子组件的行为。
存储子组件。
Component在接口中实现子相关的操作。
客户
通过Component界面操作合成中的对象。
Composite 组合的
定义有子组件的行为。
存储子组件。
Component在接口中实现子相关的操作。
Component.java
import java.util.ArrayList;
import java.util.List;
public abstract class Component
{
AccountStatement accStatement;
protected List<Component> list = new ArrayList<>();
public abstract float getBalance();
public abstract AccountStatement getStatement();
public void add(Component g) {
list.add(g);
}
public void remove(Component g) {
list.remove(g);
}
public Component getChild(int i) {
return (Component) list.get(i);
}
}
CompositeAccount.java
public class CompositeAccount extends Component
{
private float totalBalance;
private AccountStatement compositeStmt, individualStmt;
public float getBalance() {
totalBalance = 0;
for (Component f : list) {
totalBalance = totalBalance + f.getBalance();
}
return totalBalance;
}
public AccountStatement getStatement() {
for (Component f : list) {
individualStmt = f.getStatement();
compositeStmt.merge(individualStmt);
}
return compositeStmt;
}
}
AccountStatement.java
public class AccountStatement
{
public void merge(AccountStatement g)
{
//Use this function to merge all account statements
}
}
DepositAccount.java
public class DepositAccount extends Component
{
private String accountNo;
private float accountBalance;
private AccountStatement currentStmt;
public DepositAccount(String accountNo, float accountBalance) {
super();
this.accountNo = accountNo;
this.accountBalance = accountBalance;
}
public String getAccountNo() {
return accountNo;
}
public float getBalance() {
return accountBalance;
}
public AccountStatement getStatement() {
return currentStmt;
}
}
SavingsAccount.java
public class SavingsAccount extends Component
{
private String accountNo;
private float accountBalance;
private AccountStatement currentStmt;
public SavingsAccount(String accountNo, float accountBalance) {
super();
this.accountNo = accountNo;
this.accountBalance = accountBalance;
}
public String getAccountNo() {
return accountNo;
}
public float getBalance() {
return accountBalance;
}
public AccountStatement getStatement() {
return currentStmt;
}
}
Client.java
public class Client
{
public static void main(String[] args)
{
// Creating a component tree
Component component = new CompositeAccount();
// Adding all accounts of a customer to component
component.add(new DepositAccount("DA001", 100));
component.add(new DepositAccount("DA002", 150));
component.add(new SavingsAccount("SA001", 200));
// getting composite balance for the customer
float totalBalance = component.getBalance();
System.out.println("Total Balance : " + totalBalance);
AccountStatement mergedStatement = component.getStatement();
//System.out.println("Merged Statement : " + mergedStatement);
}
}
总结
- 复合模式定义了由单个对象和复合对象组成的类层次结构。
- 客户端通过使客户端代码简单的组件接口统一处理原始对象和复合对象。
- 添加新组件很容易,并且客户端代码不需要更改,因为客户端通过组件接口处理新组件。
- 可以使用迭代器设计模式遍历复合层次结构。
- 访问者设计模式可以对组合应用操作。
- 享元设计模式经常与 Composite 结合来实现共享叶节点。
装饰器设计模式
特定实例添加附加功能或行为
以下是装饰器设计模式的参与者:
Component——接口可以执行与其相关逻辑
Concrete component——实现Component接口,添加额外功能。
Decorator——一个抽象类,包含了Component对象,还实现了组件接口
Concrete Decorator —— 扩展了Decorator,本在组件类的基础上构建额外功能
Component.java
public interface Component {
String operation();
}
ConcreteComponent.java
public class ConcreteComponent implements Component{
@Override
public String operation() {
return "执行了 ConcreteComponent";
}
@Override
public void p(){
System.out.println("添加附加功能");
}
}
Decorator.java
public abstract class Decorator implements Component{
private Component component;
public Decorator(Component component){
this.component = component;
}
@Override
public String operation() {
return component.operation();
}
}
ConcreteDecorator.java
public class ConcreteDecorator extends Decorator{
public ConcreteDecorator(Component component) {
super(component);
}
@Override
public String operation() {
return addContent(super.operation());
}
@Override
public void p() {
}
private String addContent(String data){
return data + "执行了 ConcreteDecorator";
}
}
ConcreteDecorator2.java
public class ConcreteDecorator2 extends Decorator{
public ConcreteDecorator2(Component component) {
super(component);
}
@Override
public String operation() {
return addContent(super.operation());
}
@Override
public void p() {
}
private String addContent(String data){
return data + "执行了 ConcreteDecorator";
}
}
TestDecorator.java
public class TestDecorator {
public static void main(String[] args) {
ConcreteDecorator decorator = new ConcreteDecorator(new ConcreteDecorator2(new ConcreteComponent()));
System.out.println(decorator.operation());
}
}
装饰器模式面试题
如何决定何时使用装饰器模式?
发现装饰器设计模式有几个需求指标表明它是潜在的解决方案,例如
- 扩展e的对象,例如窗口控件,需要额外的可选功能,如滚动条、标题栏和状态栏
- 通过装饰支持扩展的几个对象如滚动条、标题栏和状态栏。通常,这些对象共享一个公共接口、特征或超类,有时还有其他中间超类。如滚动条、标题栏和状态栏都属于窗口控件。
- 装饰对象(类或原型实例化)和装饰器对象具有一个或几个共同特征。装饰对象和装饰器对象具有通用接口、特征或类继承。如滚动条、标题栏、状态栏和窗口控件都有共同接口e对象。
装饰器模式和适配器模式的区别
适配器模式用于将对象接口转换为其他对象。
装饰器模式用于扩展对象的功能,同时维护其接口。这两种模式有时可能被称为包装器模式,因为他们都包装了对象。
装饰器模式和子类化之间的区别
子类化中,可以装饰任何实现一个接口的类。
java中装饰器模式的常用方法
装饰器模式的常见用法
- Java iO 库类 例如 BufferedInputStream bs = new BufferedInputStream(new
FileInputStream(new File(“File1.txt”))); - 在显示标签jsp库中的装饰器列数据中,例如
<display:table name="reportsViewResultTable" class="demoClass" id="reportsQueryViewResultTable">
<display:column title="Report Id" sortable="true" property="reportDisplayId" decorator="com.comp.FirstColumnDataDecorator"></display:column>
</display:table>
- 在sitemesh中使用装饰器,以提供一致的 UI 体验。
外观设计模式
外观设计模式为子系统中的一组接口提供统一的接口。外观定义了一个更高级别的接口,使子系统更易于使用。
什么时候使用外观模式
简单化复杂系统。其目的是将内部复杂性隐藏在从外部看起来很简单的单个界面。
外观将使用的系统的代码从子系统的细节中解耦,使得以后修改系统更容易。
真实世界的门面示例
我们以一个非常简单的台式电脑机器为例。当启动计算机时,只需按下按钮,不关心计算机硬件和软件内部的所有东西。这是外观模式的一个例子。
在java编程中,连接数据库,JDBC中调用datasource.getConnection()来获取连接,例如驱动程序、创建连接或池中获取连接、新统计信息,然后将连接引用返回给调用方方法。
report.java
public class Report {
private ReportHeader header;
private ReportData data;
private ReportFooter footer;
public ReportHeader getHeader() {
return header;
}
public void setHeader(ReportHeader header) {
System.out.println("Setting report header");
this.header = header;
}
public ReportData getData() {
return data;
}
public void setData(ReportData data) {
System.out.println("Setting report data");
this.data = data;
}
public ReportFooter getFooter() {
return footer;
}
public void setFooter(ReportFooter footer) {
System.out.println("Setting report footer");
this.footer = footer;
}
}
ReportHeader.java
public class ReportHeader {
}
ReportFooter.java
public class ReportFooter {
}
ReportData.java
public class ReportData {
}
ReportType.java
public enum ReportType
{
PDF, HTML
}
ReportWriter.java
public class ReportWriter {
public void writeHtmlReport(Report report, String location) {
System.out.println("HTML Report written");
//implementation
}
public void writePdfReport(Report report, String location) {
System.out.println("Pdf Report written");
//implementation
}
}
ReportGeneratorFacade.java
import javax.activation.DataSource;
public class ReportGeneratorFacade
{
public static void generateReport(ReportType type, DataSource dataSource, String location)
{
if(type == null || dataSource == null)
{
//throw some exception
}
//Create report
Report report = new Report();
report.setHeader(new ReportHeader());
report.setFooter(new ReportFooter());
//Get data from dataSource and set to ReportData object
report.setData(new ReportData());
//Write report
ReportWriter writer = new ReportWriter();
switch(type)
{
case HTML:
writer.writeHtmlReport(report, location);
break;
case PDF:
writer.writePdfReport(report, location);
break;
}
}
}
Main.java
import com.howtodoinjava.facade.ReportGeneratorFacade;
import com.howtodoinjava.facade.ReportType;
public class Main
{
public static void main(String[] args) throws Exception
{
ReportGeneratorFacade reportGeneratorFacade = new ReportGeneratorFacade();
reportGeneratorFacade.generateReport(ReportType.HTML, null, null);
reportGeneratorFacade.generateReport(ReportType.PDF, null, null);
}
}
打印
Console
Setting report header
Setting report footer
Setting report data
HTML Report written
Setting report header
Setting report footer
Setting report data
Pdf Report written
外观模式的优点
隐藏了复杂的子系统,对外提供简单接口。
外观不限制对子系统的访问
外观不封装子系统类或接口。它只是提供了一个简单的接口(或层),让我们的生活更轻松。我们可以自由地公开子系统的任何功能或整个子系统本身。
外观模式与适配器模式
在适配器模式中,我们尝试更改接口,以便客户端能够与系统一起工作。否则系统将难以被客户使用(甚至不可用)。
外观模式简化了界面。它为客户端提供了一个简单的交互界面(而不是一个复杂的子系统)。
外观模式与中介模式
在中介者模式实现中,子系统知道中介者。他们互相交谈。
但是在外观中,子系统不知道外观,并且从外观到子系统提供单向通信。
复杂子系统只有一个外观?
一点也不。我们可以为特定的复杂子系统创建任意数量的外观。这个想法是使系统更易于使用。
外观设计模式的挑战
- 子系统与外观层相连。因此,您需要处理额外的编码层。
- 当子系统的内部结构发生变化时,您也需要将变化合并到外观层中。
享元设计模式
使用共享有效地支持大量细粒度对象。享元是可以同时在多个上下文中使用的共享对象。享元在每个上下文中充当独立对象。
何时使用享元设计模式
享元设计模式的场景:
当我们需要大量相似的对象时,这些对象仅在少数参数方面是唯一的,并且大多数通常都是通用的。
控制大量对象的内存消耗,通过创建更少对象并共享它们。
外在属性喝内在属性
享元对象本质上具有两种属性,内在属性和外在属性。
一个内在状态属性存储/共享在享元对象中,它独立于享元的上下文。作为最佳实践,内在状态应该不可变。
外部状态随享元的上下文而变化,因此不能共享。客户端对象保持外部状态,需要在对象创建期间将其传递给享元对象。
享元模式的真实世界示例
- 假设我们有一支可以有/没有refill的笔。笔芯可以是任何颜色,因此可以使用笔来创建具有 N 种颜色的绘图。
这里Pen可以是refill作为外部属性的享元对象。所有其他属性,例如笔身、指针等,都可以是所有笔共有的内在属性。一支笔将仅以其笔芯颜色来区分,没有别的。
所有需要访问红笔的应用程序模块——可以使用相同的红笔实例(共享对象)。只有当需要不同颜色的笔时,应用程序模块才会向享元工厂请求另一支笔。 - 编程中,我们可以将java.lang.String常量视为享元对象。所有字符串都存储在字符串池中,如果我们需要具有特定内容的字符串,则运行时返回对池中已经存在的字符串常量的引用——如果可用的话。
- 在浏览器中,我们可以在网页的多个位置使用图像。浏览器只会加载一次图像,其他时候浏览器会重用缓存中的图像。现在图像相同,但在多个地方使用。它的URL 是内在属性,因为它是固定的和可共享的。图像位置坐标、高度和宽度是外在属性,它们根据必须渲染的位置(上下文)而有所不同。
享元设计模式示例
在给定的示例中,我们正在构建一个画笔应用程序,其中客户端可以使用三种类型的画笔——厚、薄和中。所有粗(细或中等)画笔将以完全相同的方式绘制内容——只有内容颜色会有所不同。
Pen.java
public interface Pen
{
public void setColor(String color);
public void draw(String content);
}
BrushSize.java
public enum BrushSize {
THIN, MEDIUM, THICK
}
ThickPen.java
public class ThickPen implements Pen {
final BrushSize brushSize = BrushSize.THICK; //intrinsic state - shareable
private String color = null; //extrinsic state - supplied by client
public void setColor(String color) {
this.color = color;
}
@Override
public void draw(String content) {
System.out.println("Drawing THICK content in color : " + color);
}
}
ThinPen.java
public class ThinPen implements Pen {
final BrushSize brushSize = BrushSize.THIN;
private String color = null;
public void setColor(String color) {
this.color = color;
}
@Override
public void draw(String content) {
System.out.println("Drawing THIN content in color : " + color);
}
}
MediumPen.java
public class MediumPen implements Pen {
final BrushSize brushSize = BrushSize.MEDIUM;
private String color = null;
public void setColor(String color) {
this.color = color;
}
@Override
public void draw(String content) {
System.out.println("Drawing MEDIUM content in color : " + color);
}
}
PenFactory.java
PenFactory.java
import java.util.HashMap;
public class PenFactory
{
private static final HashMap<String, Pen> pensMap = new HashMap<>();
public static Pen getThickPen(String color)
{
String key = color + "-THICK";
Pen pen = pensMap.get(key);
if(pen != null) {
return pen;
} else {
pen = new ThickPen();
pen.setColor(color);
pensMap.put(key, pen);
}
return pen;
}
public static Pen getThinPen(String color)
{
String key = color + "-THIN";
Pen pen = pensMap.get(key);
if(pen != null) {
return pen;
} else {
pen = new ThinPen();
pen.setColor(color);
pensMap.put(key, pen);
}
return pen;
}
public static Pen getMediumPen(String color)
{
String key = color + "-MEDIUM";
Pen pen = pensMap.get(key);
if(pen != null) {
return pen;
} else {
pen = new MediumPen();
pen.setColor(color);
pensMap.put(key, pen);
}
return pen;
}
}
PaintBrushClient.java
public class PaintBrushClient
{
public static void main(String[] args)
{
Pen yellowThinPen1 = PenFactory.getThickPen("YELLOW"); //created new pen
yellowThinPen1.draw("Hello World !!");
Pen yellowThinPen2 = PenFactory.getThickPen("YELLOW"); //pen is shared
yellowThinPen2.draw("Hello World !!");
Pen blueThinPen = PenFactory.getThickPen("BLUE"); //created new pen
blueThinPen.draw("Hello World !!");
System.out.println(yellowThinPen1.hashCode());
System.out.println(yellowThinPen2.hashCode());
System.out.println(blueThinPen.hashCode());
}
}
单例模式和享元模式的区别
单例模式保证内存中只有一个对象。需要在应用程序的所有部分重用现有对象。
享元模式是根据客户端提供的额外部属性创建大量不同的相似对象。
并发对享元的影响
并发环境中创建享元对象,需要使用单例模式中的双重检查锁定,来避免可能会拥有同一个享元对象的多个实例问题。
享元设计模式的好处
减少可以相同控制的重物的内存消耗。
减少系统中“完整但相似的对象”总数。
提供一个集中的机制来控制许多虚拟对象的状态。
内在和外资啊数据是否可以共享?
内在数据可以共享,比如笔的粗细,外部数据不共享,比如笔芯的颜色。
享元模式的挑战
- 花时间配置享元。设计时间和技能开销大。
- 为了创建享元,必须提取一个通用模板类。这个额外的编码层可能很棘手,有时难以调试和维护。
- 享元模式通常和单例模式结合。
代理设计模式
在代理设计模式中,代理对象为另一个对象提供代理或占位符来控制对其的访问。代理大量用于实现延迟加载相关的用例,在实际需要之前我们不想创建完整对象。
什么时候使用代理设计模式
代理对象隐藏原始对象并能够对它的访问。当我们可能想要使用一个可以作为其他东西的接口的类时,我们可以使用代理。
代理大量用于实现延迟加载相关的用例,在实际需要之前我们不想创建完整对象。
代理也可用于在原始对象周围添加额外的安全层。
代理模式的真实示例
在hibernate中,我们编写代码来从数据库中获取实体。Hibernate 返回一个对象,该对象是底层实体类的代理(由 Hibernate 通过扩展域类动态构造)。客户端代码能够读取任何需要使用代理读取的数据。
这些代理实体类有助于实现延迟加载场景,其中关联实体仅在显式请求时才被获取。它有助于提高 DAO 操作的性能。
在企业网络中,互联网访问受到网络代理的保护。所有网络请求都通过代理,该代理首先检查允许网站的请求并将数据发布到网络。如果请求看起来可疑,代理会阻止请求——否则请求会通过。
在面向方面编程 (AOP) 中,由 AOP 框架创建的对象,用于实现方面协定(建议方法执行等)。例如,在Spring AOP中,AOP 代理将是 JDK 动态代理或 CGLIB 代理。
设计模式参与者
主题:是一个接口,它公开可供客户端使用的功能。
正式主题:是一个实现类主题,它是需要隐藏在代理后面的具体实现。
代理——通过扩展隐藏真实对象,客户端通过这个代理对象与真实对象通信。通常,当客户端请求真实对象时,框架会创建此代理对象。
代理设计模式示例
在给定的示例中,我们有一个真实对象客户端需要访问才能做某事。它将要求框架提供真实对象. 但是由于需要保护对该对象的访问,因此框架返回对真实对象代理.
对代理对象的任何调用都用于满足其他要求,并且调用将传递给真实对象。
RealObject.java
public interface RealObject
{
public void doSomething();
}
RealObjectImpl.java
public class RealObjectImpl implements RealObject {
@Override
public void doSomething() {
System.out.println("Performing work in real object");
}
}
RealObjectProxy.java
public class RealObjectProxy extends RealObjectImpl
{
@Override
public void doSomething()
{
//Perform additional logic and security
//Even we can block the operation execution
System.out.println("Delegating work on real object");
super.doSomething();
}
}
Client.java
public class Client
{
public static void main(String[] args)
{
RealObject proxy = new RealObjectProxy();
proxy.doSomething();
}
}
什么是不同类型的代理
代理通常分为四种类型 -
远程代理——需要做额外的工作,为了与远程对象交谈。理对象代表原始对象进行这种通信,而客户端则专注于真正的谈话要做。
虚拟代理——延迟昂贵对象的创建和初始化,直到需要时才按需创建对象。Hibernate 创建的代理实体是虚拟代理的示例。
保护代理——帮助实现对原始对象的安全性。他们可以在方法调用之前检查访问权限,并根据结论允许或拒绝访问。
智能代理– 当客户端访问对象时执行额外的内务处理工作。一个例子可以是在访问之前检查真实对象是否被锁定,以确保没有其他对象可以更改它。
代理模式与装饰器模式
两种模式之间的主要区别在于它们所承担的责任。装饰器专注于添加职责,而代理专注于控制对对象的访问。