技术标签: 软件设计体系结构
常常有一些组件在内部具有特定的数据结构,如果让客户程序依赖这些特定的数据结构,将极大地破坏组件的复用。这时候,将这
些特定数据结构封装在内部,在外部提供统一的接口,来实现与特定数据结构无关的访问,是一种行之有效的解决方案。
●Composite(组合模式)
●Iterator(迭代器)
●Chain of Resposibility(责任链)
组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
意图:将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
主要解决:它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
何时使用: 1、您想表示对象的部分-整体层次结构(树形结构)。 2、您希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
如何解决:树枝和叶子实现统一接口,树枝内部组合该接口。
关键代码:树枝内部组合该接口,并且含有内部属性 List,里面放 Component。
应用实例: 1、算术表达式包括操作数、操作符和另一个操作数,其中,另一个操作符也可以是操作数、操作符和另一个操作数。 2、在 JAVA AWT 和 SWING 中,对于 Button 和 Checkbox 是树叶,Container 是树枝。
优点: 1、高层模块调用简单。 2、节点自由增加。
缺点:在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。
使用场景:部分、整体场景,如树形菜单,文件、文件夹的管理。
注意事项:定义时为具体类。
这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。
在软件在某些情况下,客户代码过多地依赖于对象容器复杂的内部实现结构,对象容器内部实现结构(而非抽象接口)的变化将引
起客户代码的频繁变化,带来了代码的维护性、扩展性等弊端。
将对象组合成树形结构以表示"部分-整体"的层次结构。
Composite使得用户对单个对象和组合对象的使用具有一致性(稳定)。
Add()和Remove(),GetChild()放在Component抽象类里面是存在争议的。比如下方代码,我并没有把Add()和Remove()放在父类Component中,而是放在Composite树节点里面。放在父类里面会带来一个弊端,叶子节点Leaf类不能实现Add()和Remove(),如果去实现这两个方法,可以抛出异常说明不支持接口,但是这样毫无意义。或者实现之后做空操作,那还不如不实现。放入Composite类实现,也是有弊端的。使用add()的时候要判断对象类型是否是Component。
所以现在普遍的做法就是不在Component实现Add()和Remove(),GetChild()。放到Composite树节点里面实现。
//业务需求抽象类
public abstract class Component {
public abstract void process();
}
//树节点
public class Composite extends Component {
String name;
Vector<Component> elements;
public Composite(String s) {
name = s;
}
public void add(Component element) {
elements.add(element);
}
public void remove(Component element) {
elements.remove(element);
}
//递归调用
@Override
public void process() {
//1. 处理当前节点
//2. 处理叶子节点
for (Component e : elements) {
e.process();//多态调用
}
}
}
//叶子节点
public class Leaf extends Component {
String name;
public Leaf(String s) {
name = s;
}
@Override
public void process() {
//处理当前节点
}
}
public class ClientApp{
//如果不用Composite模式 c.process(); 不能这样调用 还需要使用typeOf c.getType 判断 c的类型
//判断c 是 Leaf 还是 Composite 然后做出相应的处理 使用 Composite模式就免去了 这些操作
public static void invoke(Component c){
//...
c.process(); //多态调用
//...
}
public static void main(String args[]) {
Composite root=new Composite("root");
Composite treeNode1=new Composite("treeNode1");
Composite treeNode2=new Composite("treeNode2");
Composite treeNode3=new Composite("treeNode3");
Composite treeNode4=new Composite("treeNode4");
Leaf leat1=new Leaf("left1");
Leaf leat2=new Leaf("left2");
root.add(treeNode1);
treeNode1.add(treeNode2);
treeNode2.add(leaf1);
root.add(treeNode3);
treeNode3.add(treeNode4);
treeNode4.add(leaf2);
invoke(root);
invoke(leaf2);
invoke(treeNode3);
}
}
View.java 可以看做是 叶子节点 或者是 Component。而 Composite 树节点 就是 ViewGroup。
在ViewGroup 体现了 Composite设计模式
ViewGroup继承View,所以ViewGroup都是树节点 。
ViewGroup 中的 mChildren 数组 对应 上方代码中的 Vector<Component> elements;
hasFocusable() 对应上方代码中的 process(),addFocusables(),findViewsWithText()也是对应process()
@Override
boolean hasFocusable(boolean allowAutoFocus, boolean dispatchExplicit) {
// This should probably be super.hasFocusable, but that would change
// behavior. Historically, we have not checked the ancestor views for
// shouldBlockFocusForTouchscreen() in ViewGroup.hasFocusable.
// Invisible and gone views are never focusable.
相当于先处理当前节点的业务逻辑
if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
return false;
}
// Only use effective focusable value when allowed.
if ((allowAutoFocus || getFocusable() != FOCUSABLE_AUTO) && isFocusable()) {
return true;
}
//相当于处理叶子节点的业务逻辑
// Determine whether we have a focused descendant.
final int descendantFocusability = getDescendantFocusability();
if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
return hasFocusableChild(dispatchExplicit); //使用递归
}
return false;
}
Composite模式采用树形结构来实现普遍存在的对象容器,从而将“一对多”的关系转化为“一对一”的关系,使得客户代码可以一致地(复用)处理对象和对象容器,无需关心处理的是单个的对象,还是组合的对象容器。
将“客户代码与复杂的对象容器结构’'解耦是Composite的核心思想,解耦之后,客户代码将与纯粹的抽象接口——而非对象容器的内部实现结构——发生依赖,从而更能‘应对变化”。
Composite模式在具体实现中,可以让父对象中的子对象反向追溯;如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率。
迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式。这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。
迭代器模式属于行为型模式。
意图:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
主要解决:不同的方式来遍历整个整合对象。
何时使用:遍历一个聚合对象。
如何解决:把在元素之间游走的责任交给迭代器,而不是聚合对象。
关键代码:定义接口:hasNext, next。
应用实例:JAVA 中的 iterator。
优点: 1、它支持以不同的方式遍历一个聚合对象。 2、迭代器简化了聚合类。 3、在同一个聚合上可以有多个遍历。 4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
缺点:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
使用场景: 1、访问一个聚合对象的内容而无须暴露它的内部表示。 2、需要为聚合对象提供多种遍历方式。 3、为遍历不同的聚合结构提供一个统一的接口。
注意事项:迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。
在软件构建过程中,集合对象内部结构常常变化各异。但对于这些集合对象,我们希望在不暴露其内部结构的同时,可以让外部客户代码透明地访问其中包含的元素;同时这种‘透明遍历”也为“同一种算法在多种集合对象上进行操作”提供了可能。
使用面向对象技术将这种遍历机制抽象为‘迭代器对象”为“应对变化中的集合对象”提供了一种优雅的方式。
提供一种方法顺序访问一个聚合对象(容器)中的各个元素,而又不暴露(稳定)该对象的内部表示。
package File;
import java.util.Iterator;
import java.util.Vector;
public class ClientApp{
public static void main(String args[]) {
Vector<String> mc=new Vector<>();
mc.add("Beijing");
mc.add("Shanghai");
mc.add("New York");
mc.add("Paris");
mc.add("London");
Iterator<String> iter = mc.iterator();
while (iter.hasNext())
{
String item = iter.next();
System.out.println(item);
}
// : 后面的类型 一定要支持迭代器的类型才行 也就是实现Iterabale接口
for(String data: mc){ //还是要干迭代器干的事
System.out.println(data);
}
//如果使用反汇编工具 这两个循环生成的代码是一样的
}
}
AbstractList内部实现 iterator() 返回一个真正的类型对象
AbstractList实现了List接口
找到了迭代器最原始的接口
回到AbstractList类中查看iterator() 中的 return 语句
Itr类是封装在AbstractList类中的内部嵌套类,实现内部类原因是对外界是没必要知道的, 设计模式的原则内部实现尽量不要暴露给外界,除非有绝对的必要性。
在类图结构中 Iterator对应代码中的是 ,First() 在 Iterator 接口中被省略了。调用了一次hasNext(),然后再调用next(),直接就能得到First。Next() 对应的 是
。IsDone() 对应代码中的 hasNext()。CurrentItem() 已经被合并在
,直接next()就返回对象。
Aggregate 是聚合对象 对应代码中的 。可迭代的对象就是聚合对象。CreateIterator() 对应代码中的
。
Concretelterator 对应代码中的 。
ConcreteAggregate 对应的是 的子类。在java库中几乎所有的集合类型,比如List,ArrayList,Set,都实现了 Iterable 接口。
Aggregate 和 Iterator 是稳定的。ConcreteAggregate 和 Concretelterator 是变化的。
迭代抽象:访问一个聚合对象的内容而无需暴露它的内部表示。
迭代多态:为遍历不同的集合结构提供一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作。
迭代器的健壮性考虑:遍历的同时更改迭代器所在的集合结构,会导致问题。(不要在迭代的过程中删除元素)
js通过对象通过value找key
前两天拿到阿里巴巴和美团的offer,算是快走过人生的一个阶段了,应该准备走向社会了。现在的自己,可能不会被当做一个反面教材,不过以前就不好说了。回想自己的大学生活,第一年挂科无数(其实是挂了学分最高的几门课),最终成绩排名专业倒数第五,现在的我学习成绩也不怎么样,这就是我与奖学金无缘的原因了。然后也在学生会混过,交了一些朋友,但也浪费了不少的时间,还好,后来学生会没再继续干下去。这两天总有人找我分享一下我求职的经验,说求职的经验,倒不如说大学的经验。找一个好的工作不是一朝一夕的事,需要长期能力的积累。
命名管道Demo说明[email protected] 该程序分为server和client,基于命名管道,使用时1个服务器端对应n个客户端,具备以下特点:1:网上不少例程只能连接一次,此程序中,服务器端维护管道状态,客户端可重复关闭、打开管道。2:界面友好,这不是界面花哨或贴个图啥的,而是比如服务器端可最小化到托盘,客户端打开管道的交互界面更符合习惯,Simplest and b...
feign之间调用服务需要加token怎么办呢,解决办法实现RequestInterceptor接口package com.java.sa.content.interceptor;import feign.RequestInterceptor;import feign.RequestTemplate;import org.springframework.web.context.req...
http://share.eepw.com.cn/share/download/id/61550
1、手动输入空格符号 &nbsp; ;需要在英文半角状态下输入2、切换至全角状态,直接使用空格键。
原题: Problem Description 有一个长度为n(n<=100)的数列,该数列定义为从2开始的递增有序偶数,现在要求你按照顺序每m个数求出一个平均值,如果最后不足m个,则以实际数量求平均值。编程输出该平均值序列 Input 输入数据有多组,每组占一行,包含两个正整...
使用vim命令打开一个文件:例如,打开openwrt系统下的system配置文件vim /etc/config/system内容如下:config system option hostname 'OpenWrt' option timezone 'UTC' option ttylogin '0' option log_size '64' option urandom_seed '0'config timeserve
Ip:明确特征的32位地址,也是每一个联网设备的唯一标识。(IPV4 <=> IPV6 128位) 书写方式为点分十进制:101010…..010101 => 80D00298(16进制) => 128.208.2.152(十进制) 网络部分:可分配的ip部分,如128.208.0.0/24,即有24位可作为网络部分分配,子网掩码,就是用于表示Ip地址的网络部分,如255.25
引言 今天想要实现一个功能,根据文件全名,获取文件真实名称。为什么会有这个功能呢,因为有的浏览器,尤其是IE,低版本在上传时,容易将文件全名带路径当做文件真实名称,比如:C:\filepath\myfile.png 上传到后台程序,实际上,后台程序不关心这个文件是存在客户端的哪个盘符下,哪个路径下。 于是我们要做的就是,把文件名称中的路径部分干掉。 好了,废话不多说,直...
今天给大家讲解的是在HTML5 canvas画带箭头的虚线。关于Canvas 对象表示一个 HTML 画布元素 -。它没有自己的行为,但是定义了一个 API 支持脚本化客户端绘图操作。本案例注意事项:1、当你拖动箭头时 canvas里面线条绘制自动重新计算点。2、canvas没有画虚线的api,因为对api不是很熟悉,所以就不献丑了,在网上找的。3、箭头出来后 点击画布里面的任意点 箭头将延伸到该...
前言 《阿里巴巴Java开发手册》是阿里巴巴集团技术团队的集体经验总结,经历了多次大规模一线实战的检验及不断的完善,反馈给广大开发者。现代软件行业的高速发展对开发者的综合素质要求越来越高,因为不仅是编程知识点,其它维度的知识点也会影响到软件的最终交付质量。比如:数据库的表结构和索引设计缺陷可能带来软件上的架构缺陷或性能风险;工程结构混乱导致后续维护艰难;没有鉴权的漏洞代码易被黑客攻击等等。