代码Github连接 :https://github.com/tangbi123/Tab-Design-Pattern
Bridge模式
将两样东西连接起来,它们分别是类的功能层次结构和类的实现层次结构。
类的层次结构的两个作用
1)希望增加新功能时
当腰增加新的功能时,我们可以从各个层次的类中找出最符合自己需求的类,然后以他为父类,编写子类,并在子类中增加新的功能。
2)希望增加新的实现
父类通过声明抽象方法来定义接口,子类实现抽象方法。
这里的类的层次接口并非用于增加功能,而是用于任务分担:
父类通过声明抽象方法来定义接口(API)
子类通过实现具体方法来实现接口(API)
1、示例
代码清单
1)DisPlayImpl
public abstract class DisplayImpl {
public abstract void rawOpen();
public abstract void rawPrin();
public abstract void rawClose();
}
2)StringDisplayImpl
public class StringDisplayImpl extends DisplayImpl {
private String string;
private int width;
public StringDisplayImpl(String string) {
this.string = string;
this.width = string.getBytes(StandardCharsets.UTF_8).length;
}
@Override
public void rawOpen() {
printLine();
}
@Override
public void rawPrin() {
System.out.println("|" + string + "|");
}
@Override
public void rawClose() {
printLine();
}
private void printLine(){
System.out.print("+");
for(int i = 0; i < width; i++){
System.out.print("-");
}
System.out.println("+");
}
}
3)Display
public class Display {
private DisplayImpl impl;
public Display(DisplayImpl impl) {
this.impl = impl;
}
public void open(){
impl.rawOpen();
}
public void print(){
impl.rawPrin();
}
public void close(){
impl.rawClose();
}
public final void display(){
open();
print();
close();
}
}
- CountDisplay
public class CountDisplay extends Display{
public CountDisplay(DisplayImpl impl) {
super(impl);
}
public void multiDisplay(int times){
open();
for(int i = 0; i < times; i++){
print();
}
close();
}
}
5)main
public class main {
public static void main(String[] args) {
Display d1 = new Display(new StringDisplayImpl("Hello, world"));
Display d2 = new CountDisplay(new StringDisplayImpl("Hello, world1"));
CountDisplay d3 = new CountDisplay(new StringDisplayImpl("Hello, world2"));
d1.display();
d2.display();
d3.display();
d3.multiDisplay(3);
}
}
2、角色
1)Abstraction(抽象化) =》 Display
最上层,使用Implemenntor角色的方法定义了基本的功能,保存了Implementor角色的实例。
2)RefinedAbstraction(增加功能的抽象化) =》 CountDisplay
在Abstraction的基础上增加新的功能(继承)。
3)Implementor(实现者) =》 DisplayImpl
位于“类的实现层次结构”的最上层。定义了实现Abstraction角色接口的方法。
4)ConcreteImplementor(具体实现者) => StringDisplayImpl
负责实现 Implementor中的接口(API)
3、思路要点
分开后更能容易扩展
Bridge 的特征是 将 类的功能层次结构 和 类的实现层次结构 分离开来, 有利于对他们进行扩展。
当想要增加功能时,只需要在类的 功能层次结构 一侧增加类即可,不必对类的 实现层次结构 做任何修改。 而且,增加后的功能 可以被 所有的实现使用。
继承是强关联,委托是弱关联
Strategy模式
使用Strategy模式可以整体地替换算法的实现部分,能让我们轻松地以不同的算法去解决同一个问题。
1、示例
两种猜拳策略:
1、如果这局猜拳获胜了,那么下一句也出一样的手势。
2、根据上一句地手势从概率上计算出下一句地手势。
代码清单
1)Hand
public class Hand {
public static final int HANDVALUE_GUU = 0; // 石头
public static final int HANDVALUE_CHO = 1; // 剪刀
public static final int HANDVALUE_PAA = 2; // 布
public static final Hand[] hand = {
new Hand(HANDVALUE_GUU),
new Hand(HANDVALUE_CHO),
new Hand(HANDVALUE_PAA)
};
private static final String[] name = {
"石头", "剪刀", "布"
};
private int handValue;
private Hand(int handValue){
this.handValue = handValue;
}
public static Hand getHand(int handValue){
return hand[handValue];
}
public boolean isStrongerThan(Hand h){
return fight(h) == 1;
}
public boolean isWeakerThan(Hand h){
return fight(h) == -1;
}
private int fight(Hand h){
if(this == h)return 0;
else if((this.handValue + 1) % 3 == h.handValue)
return 1;
else return 0;
}
public String toString(){
return name[handValue];
}
}
2)Strategy
public interface Strategy {
public abstract Hand nextHand();
public abstract void study(boolean win);
}
3)WinnerStrategy
public class WinningStrategy implements Strategy{
private Random random;
private boolean won = false;
private Hand preHand;
public WinningStrategy(int seed){
random = new Random(seed);
}
@Override
public Hand nextHand() {
if(!won){
preHand = Hand.getHand(random.nextInt(3));
}
return preHand;
}
@Override
public void study(boolean win) {
won = win;
}
}
4)ProbStrategy
public class ProbStrategy implements Strategy{
private Random random;
private int preHandValue = 0;
private int currentValue = 0;
private int[][] history = {
{1,1,1},
{1,1,1},
{1,1,1}
};
public ProbStrategy(int seed){
random = new Random(seed);
}
@Override
public Hand nextHand() {
int bet = random.nextInt(getSum(currentValue));
int handValue = 0;
if(bet < history[currentValue][0]){
handValue = 0;
}
else if(bet < history[currentValue][0] + history[currentValue][1])
{
handValue = 1;
}
else handValue = 2;
preHandValue = currentValue;
currentValue = handValue;
return Hand.getHand((handValue));
}
private int getSum(int hv){
int sum = 0;
for(int i = 0; i < 3; i++){
sum += history[hv][i];
}
return sum;
}
@Override
public void study(boolean win) {
if(win){
history[preHandValue][currentValue]++;
}
else{
history[preHandValue][(currentValue + 1) % 3]++;
history[preHandValue][(currentValue + 2) % 3]++;
}
}
}
5)Player
public class Player {
private String name;
private Strategy strategy;
private int winCount;
private int loseCount;
private int gameCount;
public Player(String name, Strategy strategy) {
this.name = name;
this.strategy = strategy;
}
public Hand nextHand(){
return strategy.nextHand();
}
public void win(){
strategy.study(true);
winCount++;
gameCount++;
}
public void lose(){
strategy.study(false);
loseCount++;
gameCount++;
}
public void even(){
gameCount++;
}
@Override
public String toString() {
return "Player{" +
"name='" + name + '\'' +
", strategy=" + strategy +
", winCount=" + winCount +
", loseCount=" + loseCount +
", gameCount=" + gameCount +
'}';
}
}
6)Main
public class StrategyMain {
public static void main(String[] args) {
if(args.length != 2){
System.out.println("Usage: java main randomseed1 randomseed2");
System.out.println("Example: java main 314 315");
System.exit(0);
}
int seed1 = Integer.parseInt(args[0]);
int seed2 = Integer.parseInt(args[1]);
Player player1 = new Player("Taro", new WinningStrategy(seed1));
Player player2 = new Player("Hana", new ProbStrategy(seed2));
for(int i = 0; i < 100; i++){
Hand nextHand1 = player1.nextHand();
Hand nextHand2 = player2.nextHand();
if(nextHand1.isStrongerThan(nextHand2)){
System.out.println("Winner: " + player1);
player1.win();
player2.lose();
}
else if(nextHand2.isStrongerThan(nextHand1)){
System.out.println("Winner: " + player2);
player2.win();
player1.lose();
}
else{
System.out.println("Even ... ");
player1.even();
player2.even();
}
}
System.out.println("Total results: ");
System.out.println(player1.toString());
System.out.println(player2.toString());
}
}
2、角色
1)Strategy =》 Strategy接口
负责决定实现策略所必须地接口(API)。
2)ConcreteStrategy(具体决策类) =》WinnerStrategy, ProbStrategy
负责实现Strategy角色的接口(API),负责实现具体的策略。
3)Context(上下文) =》 Player
负责使用Stategy角色,Context角色保存了ConcreteStrategy角色的实例,并使用ConcreteStrategy角色去实现需求。
3、思路要点
为什么需要特意编写strategy角色
Strategy模式特意将 算法与其他部分分离开来, 只是定义了与算法相关的接口(API),然后再程序中以委托的方式来使用算法。
当我们想改善算法时,就不必修改Strategy角色的接口(API)了,仅仅修改ConcreteStrategy角色即可。 使用委托这种弱关联关系可以很方便地整体替换算法。