耦合度(Coupling):是模块之间依赖性的度量。如果一个模块中的更改可能需要另一个模块中的更改,则两个模块之间存在依赖关系
模块之间的耦合度由以下因素决定:
聚合度(Cohesion):是衡量模块的功能或职责之间关联程度的一个指标。如果模块的所有元素都朝着同一个目标工作,则该模块具有很高的内聚性。
最好的设计在模块内具有高内聚和模块间的低耦合。
OO设计的两大武器:
抽象(abstraction):模块之间通过抽象隔离开来,将稳定部分和容易变化部分分开
分离(separation):Keep It Simple, Stupid(KISS)
责任:变化的原因。
不应有多于1个的原因使得一个类发生变化,即一个类一个责任。
如果一个类包含了多个责任,那么将引起不良后果:
最简单的原则,却是最难做好的原则
面向变化的OCP
对扩展性的开放:模块的行为应是可扩展的,从而该模块可表现出新的行为以满足需求的变化
对修改的封闭:模块自身的代码是不应被修改的。扩展模块行为的一般途径是修改模块的内部实现,如果一个模块不能被修改,那么它通常被认为是具有固定的行为。
Key:抽象技术,运用接口和composition/委托
Note.一堆if-else/switch-case结构,维护起来非常麻烦。OCP
子类型必须能够替换其基类型(功能上的完全替换)。
派生类必须能够通过其基类的接口使用,客户端无需了解两者之间的差异。
更多了解Liskov替换原则,请点击此查看LSP
不能强迫客户端依赖于它们不需要的接口:只提供必需的接口。(客户端不应依赖于它们不需要的方法)
“fat”接口具有很多缺点:不够聚合
class Copy{
void Copy(OutputStream dev){
int c;
while((c=ReadKeyboard())!=EOF){
if(dev==printer)
writeToPrinter(c);
else
writeToDisk(c);
}
}
}
interface Reader{
public int read();
}
interface Writer{
public int write(c);
}
class Copy{
void Copy(Reader r,Writer w){
int c;
while(c=r.read()!=EOF)
w.write(c);
}
}
当client不知道/不确定要创建哪个具体类的实例,或者不想在client代码中指明要具体创建的实例时,用工厂方法。
定义一个用于创建对象的接口,让该接口的子类型来决定实例化哪一个类,从而使一个类的实例化延迟到其子类。
eg.第一种实现方式
interface TraceFactory{
Trace getTrace();
void otherOperation();
}
public class SystemTraceFactory implements TraceFactory{
public Trace getTrace(){
...//other operations
return new SystemTrace();
}
}
public class FileTraceFactory implements TraceFactory{
public Trace getTrace(){
return new FileTrace()
}
}
//client使用的时候
//...some code...
Trace log1=new SystemTraceFactory().getTrace();
log1.debug("entring log");
Trace log2=new FileTraceFactory().getTrace();
log2.debug("...");
第二种实现方式
interface TraceFactory{
Trace getTrace(String type);
void otherOperation();
}
public class Factory implements TraceFactory{
public getTrace(String type){
if(type.equals("file"))
return new FileTrace();
else if(type.equals("system"))
return new SystemTrace();
}
}
Trace log=new Factory().getTrace("system");
log.setDebug(false);
log.debug("...");
静态工厂方法:在方法中加上static。即可以在ADT内部实现,也可以构造单独的静态类。
eg.
public class SystemTraceFactory{
public static Trace getTrace(){
return new SystemTrace();
}
}
public class TraceFactory{
public static Trace getTrace(String type){
if(type.equals("file"))
return new FileTrace();
else if(type.equals("system"))
return new SystemTrace();
}
}
//...some code...
Trace log1=SystemTraceFactory.getTrace();
log1.setDebug(true);
log1.debug("entring log");
Trace log2=TraceFactory.getTrace("system");
log2.setDebug(true);
log2.debug("...");
相比于通过构造器(new)构造对象:
OCP:对工厂方法进行修改,对client封闭
提供接口以创建一组相关/相互依赖的对象,但不需要指明其具体实现类
Approach:通过一个可调用其他多个工厂的工厂
点击此查看详细的抽象工厂模式内容
Note.创建的不是一个完整产品,而是“产品族”(遵循固定搭配规则的多类产品的实例),得到的结果是:多个不同产品的object,各产品创建过程对client可见,但“搭配”不能改变。
本质上,Abstract Factory是把多类产品的factory method组合在一起
某个对象比较“敏感”/“私密”/“贵重”,不希望被client直接访问到,故设置proxy,在两者之间建立防火墙。
类型:
public interface Image{
void display();
}
public class ProxyImage implements Image{
private Image realImage;
private String fileName;
public ProxyImage(String fileName){
this.fileName=fileName;
}
@Override
public void display(){
if(realImage==null){
realImage=new RealImage(fileName);
}
realImage.display();
}
}
Client:
Image image=new ProxyImage("pic.jpg");
image.dispaly();
Adaptor VS Proxy
eg.
public class Subject{
private List<Observer> observers=new ArrayList<Observer>();
private int state;
public int getState(){return state;}
public void setState(int state){
this.state=state;
notifyAllObservers();//在自己状态变化时,通知所有“粉丝”
}
public void attach(Observer observer){
observers.add(observer);
}
private void notifyAllObservers(){
for(Observer observer:observers){
observer.update();
//callback调用“粉丝”的update操作,向粉丝“广播”自己的变化,实际执行delegation
}
}
}
public abstract class Observer{
protected Subject subject;
public abstract void update();
}
public class BinaryObserver extends Observer{
public BinaryObserver(Subject subject){
this.subject=subject;
this.subject.attach(this);
//构造时,指定自己的“偶像”subject,把自己注册给它,这是反向代理/委托delegation
}
@Override
public void update(){
System.out.println("Binary String:"+
Integer.toBinaryString(
subject.getState()));
}
}
//客户端
public class ObserverPatternDemo{
public static void main(String[] args){
Subject subject=new Subject();
new HexaObserver(subject);
new OctalObserver(subject);
new BinaryObserver(subject);
System.out.println("Frist state change:15");
subject.setState(15);
System.out.println("Second state change:10");
subject.setState(10);
}
}
对特定类型object的特定操作(visit),在运行时将二者动态绑定到一起,该操作可以灵活更改,无需更改被visit的类。
本质上:将数据和作用于数据上的某种/些特定操作分离开来。
/*Abstract elemnt interface(visitable)*/
public interface ItemElement{
public int accept(ShoppingCartVisitor visitor);
}
/*Concrete element*/
public class Book immplements IteamElement{
//声明自己是可以被visit的
private double price;
...
int accept(ShoppingCartVisitor visitor){
visitor.visit(this);//反向代理
//将处理数据的功能delegate到外部传入的visitor中
}
}
public class Fruit implements ItemElemnt{
private double weight;
...
int accept(ShoppingCartVisitor visitor){
visitor.visit(this);
}
}
/*Abstract visitor interface*/
public interface ShoppingCartVisitor{
int visit(Book book);
int visit(Fruit fruit);
}
public class ShoppingCartVisitorImpl implements ShoppingCartVisitor{
//这里只列出了一种visitor实现
public int visit(Book book){
//这个visit操作的功能完全可以在Book类内实现为一个方法,但这样就不可变了
int cost=0;
if(book.getPrice()>50){
cost=book.getPrice()-5;
}
else
cost=book.getPrice();
System.out.println("Book ISBN:"+book.getIsbnNumber()+" cost ="+cost);
return cost;
}
public int visit(Fruit fruit){
int cost=fruit.getPricePerKg()*fruit.getWeight();
System.out.println(fruit.getName()+" cost ="+cost);
return cost;
}
}
public class ShoppingCartClient{
public static void main(String[] args){
ItemElement[] items=new ItemElement[]{
new Book(20,"1234"),new Book(100,"5678"),
new Fruit(10,2,"Banana"),new Fruit(5,5,"Apple")
};
int total=calculatePrice(items);
System.out.println("Total Cost="+total);
}
private static int calculatePrice(ItemElement[] items){
ShoppingCarVisitor visitor=new ShoppingCartVisitorImpl();
int sum=0;
for(ItemElement item:items)
sum=sum+item.accept(visitor);
return sum;
}
}
Iterator VS Visitor
Strategy VS Visitor:二者都是通过delegation建立两个对象的动态联系
状态模式允许在运行时修改对象的行为或状态,每个行为(一组方法构成)用一个状态类表达
详细点击此查看状态模式
记住对象的历史状态,以便于“回滚”
Memento:非常简单的类,只记录一个历史状态
class Memento{
private State state;
private Memento(State state){
this.state=state;
}
public State getState(){
return state;
}
}
class Originator{
private State state;
public void setState(State state){
//ADT原本的状态转换功能,可能更复杂(例如State模式)
System.out.println("Originator: Setting state to "+state.toString());
this.state=state;
}
public Memento save(){
//保存历史状态,delegate到memento去实现
System.out.println("Originator: Saving to Memento.");
return new Memento(state);
}
public void restore(Memento m){
//利用传入的Memento对象来恢复历史状态
state=m.getState();
System.out.println("Originator: State after restoring from Memento: "+state);
}
}
class Caretaker{
private List<Memento> mementos=new ArrayList<>();//保留一系列历史状态
public void addMemento(Memento m){
mementos.add(m);//添加一个新的历史状态
}
public Memento getMemento(int i){//取回需要回滚的状态
if(mementos.size()-i<0)
throw new RuntimeException("Cannot rollback so many back!");
return mementos.get(mementos.size()-i);
//但这里有个潜在bug
}
}
public class Demonstration {
public static void main(String[] args) {
Caretaker caretaker = new Caretaker();
Originator originator = new Originator();
originator.setState("State2");
caretaker.addMemento( originator.save() );
originator.setState("State3");
caretaker.addMemento( originator.save() );
originator.setState("State4");
originator.restore( caretaker.getMemento(2) );
}
}
语法
使用grammar判断字符串是否合法,并解析成程序里使用的数据结构,通常是递归的数据结构。
url ::=‘http://’ hostname ‘/’
hostname ::= ‘mit.edu’ | ‘stanford.edu’ | ‘google.com’
其中,url是根节点(特殊的非终止符),与’google.com’相似的是终止符,hosname是非终止符
操作:
*?+ 优先级最高,连接次之,|最低
正则语法:简化之后可以表达为一个产生式而不包含任何非终止节点
一些表达:
Using Regular expressions in Java
主要方法:String.split,String.matches,java.util.regex.Pattern
Pattern对象是对regex正则表达式进行编译之后得到的结果
Matcher对象:利用Pattern对输入字符串进行解析
Extract part of an HTML tag:
flag:详细的正则语言会在后面的博客中会写