设计模式剖析系列之静态工厂方法

By heiry on 2019-08-05 [ in 技术 ]

作用:在Think in java 系列书籍以及网上大量的文章都写了其各种各样的好处,我认为言过其实,我们用静态工厂方法,主要原因在于可以根据逻辑需求返回实例对象。可以方便实现工厂模式、单例模式。其它如,有语义方法名、返回其返回类型的任何子类型的对象,方便创建参数化类型实例等等都是无关痛痒的东西。

实现:

将创建实例封装在一个工厂类的静态方法中,静态方法不同的参数返回不同的实例。

示例:

如下所示,电脑工厂类,通过提供静态方法getComputer(String Brand)生产不同品牌的电脑(返回Computer实例对象),客户端只需要调用静态工厂方法传入不同参数即可获得目标对象。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package staticfactory;
public interface Computer {
public abstract void cal();
}
package staticfactory; public interface Computer { public abstract void cal(); }
package staticfactory;
public interface Computer {
  public abstract void cal();
}
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package staticfactory;
public class Mac implements Computer {
@Overrid
public void cal() {
System.out.println("苹果电脑启动。。。。");
}
}
package staticfactory; public class Mac implements Computer { @Overrid public void cal() { System.out.println("苹果电脑启动。。。。"); } }
package staticfactory;
public class Mac implements Computer {
  @Overrid
  public void cal() {
    System.out.println("苹果电脑启动。。。。");
  }
}
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package staticfactory;
public class HP implements Computer {
@Override
public void cal() {
System.out.println("惠普电脑启动。。。。");
}
}
package staticfactory; public class HP implements Computer { @Override public void cal() { System.out.println("惠普电脑启动。。。。"); } }
package staticfactory;
public class HP implements Computer {
  @Override
  public void cal() {
    System.out.println("惠普电脑启动。。。。");

  }
}
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package staticfactory;
public class ComputerFactory {
public static Computer getComputer(String computerBrand) {
switch (computerBrand) {
case "Mac":
return new Mac();
case "HP":
return new HP();
default:
return null;
}
}
}
package staticfactory; public class ComputerFactory { public static Computer getComputer(String computerBrand) { switch (computerBrand) { case "Mac": return new Mac(); case "HP": return new HP(); default: return null; } } }
package staticfactory;
public class ComputerFactory {	
  public static Computer getComputer(String computerBrand) {
    switch (computerBrand) {
    case "Mac":
      return new Mac();
    case "HP":
      return new HP();
    default:
      return null;
    }
  }
}
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package staticfactory;
public class Client {
public static void main(String[] args) {
Computer computer = ComputerFactory.getComputer("Mac");
Computer computer2 = ComputerFactory.getComputer("HP");
computer.cal();
computer2.cal();
}
}
package staticfactory; public class Client { public static void main(String[] args) { Computer computer = ComputerFactory.getComputer("Mac"); Computer computer2 = ComputerFactory.getComputer("HP"); computer.cal(); computer2.cal(); } }
package staticfactory;

public class Client {
  public static void main(String[] args) {
    Computer computer = ComputerFactory.getComputer("Mac");
    Computer computer2 = ComputerFactory.getComputer("HP");
    computer.cal();
    computer2.cal();
  }
}

运行结果:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
苹果电脑启动。。。。
惠普电脑启动。。。。
苹果电脑启动。。。。 惠普电脑启动。。。。
苹果电脑启动。。。。
惠普电脑启动。。。。

 

>> 阅读全文  >>

设计模式剖析系列之面向对象设计原则

By heiry on 2019-08-04 [ in 技术 ]

一.开闭原则

实现方式:

1. 遵循“抽象约束、封装变化”原则,通过接口或者抽象类为实体定义一个相对稳定的抽象层,而将相同的可变因素封装在相同的具体实现类中。

2.参数类型、引用对象尽量使用接口或者抽象类,而不是实现类。

3.缩小功能颗粒度。功能粒度越小,被复用的可能性就越大,重复利用可能性就越高。

二. 里氏替换原则

简单地说,把父类都替换成它的子类,程序的行为没有变化。只要出现父类的地方,均可用子类替换,而且对整体实现并没有任何影响,提高代码的复用性。

实现方式:

子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。

三. 依赖倒置原则

——高层模块不应该依赖低层模块,两者都应该依赖其抽象

——抽象不应该依赖细节

——细节应该依赖抽象

实现方式:

1.每个类尽量提供接口或抽象类,或者两者都具备。

2.变量的声明类型尽量是接口或者是抽象,模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的。

3.任何类都不应该从具体类派生。

4.尽量不要重写基类已经写好的方法(符合里氏替换原则)。

>> 阅读全文  >>

设计模式剖析系列之UML类关系

By heiry on 2019-08-04 [ in 技术 ]

设计模式实质上并没有新的东西,只是一种思想体现,来来去去都是在围绕继承、多态、静态之间做文章。很多人学设计模式很吃力,被其中类之间复杂的引用关系绕晕,我认为掌握设计模式最重要的一步就是把常见类之间关系梳理出来,而UML早已经帮我们做了,拿来理顺即可。

一. 依赖关系Dependency

依赖关系是一种临时关系、使用的弱关系,是耦合度最低的一种关系。

代码表现:一个类的方法通过局部变量、方法的参数或者对静态方法的调用来访问另一个类(被依赖类)。下例中,猎人(Hunter)和枪(Gun)之间是一种使用关系,猎人使用枪进行射击,枪Gun作为一个参数出现在猎人shoot方法中(Hunter类中猎杀动作shoot()调用了Gun实例的开火fire()方法):

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.mosang.umlmodule;
public class Hunter {
private String name;
public void shoot(Gun g) {
g.fire();
}
}
package com.mosang.umlmodule; public class Hunter { private String name; public void shoot(Gun g) { g.fire(); } }
package com.mosang.umlmodule;

public class Hunter {
  private String name;
  public void shoot(Gun g) {
    g.fire();
  }

}
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.mosang.umlmodule;
public class Gun {
private String brand;
public void fire() {
System.out.println("shoot.....");
}
}
package com.mosang.umlmodule; public class Gun { private String brand; public void fire() { System.out.println("shoot....."); } }
package com.mosang.umlmodule;

public class Gun {
  private String brand;
  public void fire() {
    System.out.println("shoot.....");
  }
}
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.mosang.umlmodule;
public class Client {
public static void main(String[] args) {
Hunter hunter = new Hunter();
hunter.shoot(new Gun());
}
}
package com.mosang.umlmodule; public class Client { public static void main(String[] args) { Hunter hunter = new Hunter(); hunter.shoot(new Gun()); } }
package com.mosang.umlmodule;

public class Client {

  public static void main(String[] args) {
    Hunter hunter = new Hunter();
    hunter.shoot(new Gun());

  }
}

UML图示:

二. 关联关系Association

关联关系是对象之间的一种引用关系,用于表示一类对象与另一类对象之间的联系,如学生与老师、公司与员工、宠物与主人等等,存在一对一,一对多,多对多等关系。

代码表现:一个类的对象作为另一个类的成员变量。下例中学生与老师是一种多对多的关系(一个学生同时有多个老师教,一个老师也同时教多个学生),Student类作为成员变量出现在Teacher类中,Teacher类也作为成员变量出现在Student类中:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.mosang.umlmodule;
import java.util.List;
public class Student {
private String name;
private List<Teacher> teachers;
public void study() {
}
}
package com.mosang.umlmodule; import java.util.List; public class Student { private String name; private List<Teacher> teachers; public void study() { } }
package com.mosang.umlmodule;
import java.util.List;

public class Student {
  private String name;
  private List<Teacher> teachers;
  public void study() {
    
  }
}
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.mosang.umlmodule;
import java.util.List;
public class Teacher {
private String name;
private List<Student> students;
public void teach() {
}
}
package com.mosang.umlmodule; import java.util.List; public class Teacher { private String name; private List<Student> students; public void teach() { } }
package com.mosang.umlmodule;
import java.util.List;

public class Teacher {
  private String name;
  private List<Student> students;
  public void teach() {
    
  }
}

UML图示:

【双向的关联可以用带两个箭头或者没有箭头的实线来表示,单向的关联用带一个箭头的实线来表示,箭头从使用类指向被关联的类。也可以在关联线的两端标注角色名,代表两种不同的角色。】 (更多…)

>> 阅读全文  >>

静态隐藏与覆盖的不同

By heiry on 2019-08-04 [ in 技术 ]

java中,静态方法属于类,直接使用”类名.静态方法”方式访问,也可以通过”实例.方法”访问,后者一般不推荐,但是也是合法的:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.mosang.staticInheri;
public class Fu {
public String a="fu";
public void ordinaryMethod() {
System.out.println("Fu ordinaryMethod");
}
public static void staticMethod() {
System.out.println("Fu static method");
}
}
package com.mosang.staticInheri; public class Fu { public String a="fu"; public void ordinaryMethod() { System.out.println("Fu ordinaryMethod"); } public static void staticMethod() { System.out.println("Fu static method"); } }
package com.mosang.staticInheri;

public class Fu {
  public String a="fu";
  public void ordinaryMethod() {
    System.out.println("Fu ordinaryMethod");
    
  }
  public static void staticMethod() {
    System.out.println("Fu static method");
  }

}
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.mosang.staticInheri;
public class Demo {
public static void main(String[] args) {
Fu f=new Fu();
f.staticMethod();//使用实例方式访问
Fu.staticMethod();//使用类名直接访问
}
}
package com.mosang.staticInheri; public class Demo { public static void main(String[] args) { Fu f=new Fu(); f.staticMethod();//使用实例方式访问 Fu.staticMethod();//使用类名直接访问 } }
package com.mosang.staticInheri;

public class Demo {
  public static void main(String[] args) {
    Fu f=new Fu();
    f.staticMethod();//使用实例方式访问
    Fu.staticMethod();//使用类名直接访问
  }

}

输出如下:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Fu static method
Fu static method
Fu static method Fu static method
Fu static method
Fu static method

需要注意的是,在存在静态方法情况下的继承,静态方法不能被子类覆盖,即使子类有与父类相同的静态方法(同覆盖规则),也不是覆盖重写,如有Zi类继承如下:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.mosang.staticInheri;
public class Zi extends Fu{
public String a="zi";
public void ordinaryMethod() {
System.out.println("Zi ordinaryMethod"); //实例方法,覆盖重写
}
public static void staticMethod() {
System.out.println("Zi static method"); //与父类相同的静态方法(方法名、返回值、参数均相同)
}
}
package com.mosang.staticInheri; public class Zi extends Fu{ public String a="zi"; public void ordinaryMethod() { System.out.println("Zi ordinaryMethod"); //实例方法,覆盖重写 } public static void staticMethod() { System.out.println("Zi static method"); //与父类相同的静态方法(方法名、返回值、参数均相同) } }
package com.mosang.staticInheri;

public class Zi extends Fu{
  public String a="zi";
  public void ordinaryMethod() {
    System.out.println("Zi ordinaryMethod"); //实例方法,覆盖重写
    
  }
  public static void staticMethod() {
    System.out.println("Zi static method"); //与父类相同的静态方法(方法名、返回值、参数均相同)
  }

}

通过两种方式创建子类实例:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.mosang.staticInheri;
public class Demo {
public static void main(String[] args) {
Fu f1=new Zi();//多态方式创建对象
Zi f2=new Zi();//普通方式创建对象
f1.ordinaryMethod(); //输出Zi ordinaryMethod
f1.staticMethod();//输出Fu static method
f2.ordinaryMethod();//Zi ordinaryMethod
f2.staticMethod();//Zi static method
}
}
package com.mosang.staticInheri; public class Demo { public static void main(String[] args) { Fu f1=new Zi();//多态方式创建对象 Zi f2=new Zi();//普通方式创建对象 f1.ordinaryMethod(); //输出Zi ordinaryMethod f1.staticMethod();//输出Fu static method f2.ordinaryMethod();//Zi ordinaryMethod f2.staticMethod();//Zi static method } }
package com.mosang.staticInheri;

public class Demo {
  public static void main(String[] args) {
    Fu f1=new Zi();//多态方式创建对象
    Zi f2=new Zi();//普通方式创建对象
    f1.ordinaryMethod(); //输出Zi ordinaryMethod
    f1.staticMethod();//输出Fu static method
    f2.ordinaryMethod();//Zi ordinaryMethod
    f2.staticMethod();//Zi static method
  }

}

输出结果

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Zi ordinaryMethod
Fu static method
Zi ordinaryMethod
Zi static method
Zi ordinaryMethod Fu static method Zi ordinaryMethod Zi static method
Zi ordinaryMethod
Fu static method
Zi ordinaryMethod
Zi static method

从上输出结果可看到,父类的静态方法并不能被覆盖,只是被隐藏。

同样,静态成员变量也是一样的道理。出现相同静态字段时父类字段不会将子类字段覆盖,而只是将其“隐藏”。

>> 阅读全文  >>

史上最简明易懂的正则表达式教程08

By heiry on 2019-05-04 [ in 技术 ]

1.运算符优先级

默认情况下,和算术表达式类似,正则表达式按照从左到右顺序计算或匹配,相同优先级的从左到右进行运算,不同优先级的运算先高后低。

从高到低各种正则表达式运算符的优先级如下所示(注:表格数据来源于w3school):

或运算符(也称为选择运算符)在正则表达式中具有最低的优先级,也就是说,它告诉引擎要么匹配选择符左边的所有表达式,要么匹配右边的所有表达式。

比如有正则表达式 abc|edf,按照优先级,它要么匹配abc要么匹配edf,而不是在c和e之间择其一。

 

2. 零宽度断言

正则表达式中,有一类是标明位置而不匹配具体字符的特殊标识符。

>> 阅读全文  >>

史上最简明易懂的正则表达式教程07

By heiry on 2019-05-03 [ in 技术 ]

1.  数量限定符

数量限定符用来指定其紧邻的前表达式的出现次数,数量限定符主要有如下:

以上表格是多数书籍或网上教程对限定符的释义。其中最容易令人产生疑惑的“重复”二字,初学者往往理解为相同内容出现次数,个人认为这个词用得很不好。

对于正则表达式 \d{3},等同于\d\d\d,但用“重复”来强调,容易让人理解为相同结果重复三次,类似于(\d)\1\1的效果。

举例来说,\d{3},匹配123、673、222,但(\d)\1\1只能匹配这三个字符串中的222。而很多初学者往往理解“重复”为后者。

所以,有必要强调,限定符的重复次数是指表达式重复而不是结果重复。

值得注意的是,上下限限定符可以有{n}、{n,m}、{n,}等形式,但是不存在{,m}这种形式,这是一种错误的写法。另外,上下限量词与逗号之间不能有空格,如\d{3, 6}是错误的写法,将无法实现目标匹配:

 

2.贪婪模式与懒惰模式

“{m,n}”、“{m,}”、“?”、“*”和“+” 为贪婪模式的限定词,也叫做匹配优先量词。被匹配优先量词修饰的子表达式使用的就是贪婪模式,如“(Exp)+”。

在匹配优先量词后加上“?”,即变成属于非贪婪模式的量词,也叫做忽略优先量词,包括:

“{m,n}?”、“{m,}?”、“??”、“*?”  与 “+?”

贪婪模式总是试图尽可能多地匹配目标字符串,如以下HTML代码:

abc<p>第一段</p><p>第二段</p><p>第三段</p>def

我们用正则<p>.*</p> 去匹配

这是一种贪婪模式的匹配得到的匹配结果为:

 

贪婪模式下它会选定尽可能多的内容,如果 失败则回退一个字符,然后再次尝试回退的过程就叫做回溯,它会每次回退一个字符,直到找到匹配的内容或者没有字符可以回退。用通俗一点的话来说,在拿不准是否要匹配的情况下,优先尝试匹配,并记下这个状态,以备将来“后悔”(也就是回溯)。

我们把正则更改为<p>.*?</p> ,匹配结果如下:

可以看到,在懒惰模式下,它会尽可能少地重复限定量,只要匹配到一个就返回了结果。

我们再更改下正则:<p>.*?</p>def,再看匹配结果:

可以看到,在“整个表达式匹配成功”的前提下,非贪婪模式才真正的影响着子表达式的匹配行为,如果整个表达式匹配失败,非贪婪模式无法影响子表达式的匹配行为。

>> 阅读全文  >>

史上最简明易懂的正则表达式教程06

By heiry on 2019-05-03 [ in 技术 ]

1. 分组与向后引用

把正则表达式的一部分放在圆括号内,每个括号内的表达式就是一个组。

对于每个组,系统会自动给之一个组编号以便在后文引用,第一个编为1号,第二个为2号,以此类推。。。

例如,对于正则表达式 (agriculture|industry)-(China|Japan|India)其结构如下:

在表达式的后文,以使用\num的格式引用某个分组(其中num为正整数,表示第几个分组),如 (agriculture|industry)-(China|Japan|India)-\1,其结构变成了如下图:

(agriculture|industry)-(China|Japan|India)-\1能匹配字符串agriculture-China-agriculture:

其中\1就是第一个分组的引用,以agriculture开头就必须以agriculture结束。

如果我们不想使用系统自动生成的编号,或者由于组数太多不便一个个数,可以使用给组命名的方式实现向后引用,命名格式为:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
(?<groupName>expression)
(?<groupName>expression)
(?<groupName>expression)

引用格式为:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
\k<groupName>
\k<groupName>
\k<groupName>

如有如下正则表达式,可匹配HTML的标签:

^<(.+)\s.*>\w+<\/\1>$

其中:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
^ 表示行的开始
< 表示HTML的开始标签
(.+) 为标签名,如可匹配a、ul、font、...等标签名
\s 表示一个空格
.* 表示标签内的任意字符,可以匹配属性及属性值等内容
\w+ 为标签内的文字
<\/为结束标签标记 </
\1 向前引用标签名
> 封闭结束标签
$ 表示行的结束
^ 表示行的开始 < 表示HTML的开始标签 (.+) 为标签名,如可匹配a、ul、font、...等标签名 \s 表示一个空格 .* 表示标签内的任意字符,可以匹配属性及属性值等内容 \w+ 为标签内的文字 <\/为结束标签标记 </ \1 向前引用标签名 > 封闭结束标签 $ 表示行的结束
^ 表示行的开始

< 表示HTML的开始标签

(.+) 为标签名,如可匹配a、ul、font、...等标签名

\s 表示一个空格
 
.* 表示标签内的任意字符,可以匹配属性及属性值等内容

\w+ 为标签内的文字

<\/为结束标签标记 </

\1 向前引用标签名

> 封闭结束标签

$ 表示行的结束

结构如下:

匹配效果如下:

需要注意的是,不同语言实现的向后引用格式有所差异:

图片来源《正则指引》一书(作者:余晟)

如果我们使用自命名组,那么正则表达式可更改为^<(?<tagName>.+)\s.*>\w+<\/\k<tagName>>$

可以看到匹配效果是一样的:

需要注意的是,命名分组不是正则的通用规则,不同语言支持组命名的书写格式有很大区别:

图片来源《正则指引》一书(作者:余晟)

2. 取消默认分组存储

在默认情况下,只要用()框定的内容,正则引擎就会自动分配组号,存储匹配分组,这样降低了引擎速度。如果我们不需要向后引用,可以通过(?:value)语法取消默认编组存储,其中“(”后面紧跟的“?:”会告诉引擎对于组(Value),不存储匹配的值以供后向引用。

如([0-9a-z]*)-(?:\d+),明确了第二个分组不进行编号存储,不提供向后引用。

 

>> 阅读全文  >>


© 2009-2024 MOSANG.NET DESIGNED BY HEIRY