訪問者模式 Visitor 行為型 設計模式(二十七)

来源:https://www.cnblogs.com/noteless/archive/2018/12/27/10185359.html
-Advertisement-
Play Games

本文可能是你看過的最易懂的訪問者設計模式的文章之一,以示例為基礎,不斷演化,深入訪問者模式的內核,給出了訪問者模式的意圖,結構,示例代碼。 ...


訪問者模式 Visitor    image_5c2481f3_6073 《俠客行》是當代作家金庸創作的長篇武俠小說,新版電視劇《俠客行》中,開篇有一段獨白:  茫茫海外,傳說有座俠客島,島上賞善罰惡二使,每隔十年必到中原武林,向各大門派下發放賞善罰惡令, 強邀掌門人赴島喝臘八粥,拒接令者,皆造屠戮,無一幸免,接令而去者,杳無音訊,生死未僕,俠客島之行,已被視為死亡之旅。” 不過話說電視劇,我總是覺得老版的好看。

意圖

表示一個作用於某對象結構中的各元素的操作。 它使你可以在不改變各元素類的前提下定義作用於這些元素的新操作。

意圖解析

我們以代碼描述一下《俠客行》中的這個場景 假定賞善罰惡二使,一個叫做張三,一個叫做李四,面對一眾掌門 張三負責賞善,對好人賞賜,壞人他不處理; 相反,李四負責罰惡,好人不處理,對壞人懲罰

俠客行代碼示例

定義了“掌門人”介面
package visitor.俠客行;
public interface 掌門人 {

}
“掌門人”有兩種類型 沒做過壞事的掌門做過壞事的掌門
package visitor.俠客行;

public class 沒做過壞事的掌門 implements 掌門人 {

}
package visitor.俠客行;

public class 做過壞事的掌門 implements 掌門人 {

}
定義了俠客島,俠客島管理維護“江湖的掌門人”,使用List 提供了掌門人的添加方法  “add掌門人(掌門人 某掌門)” 定義了“賞善罰惡(String 處理人)”方法,用於賞善罰惡,接受參數為處理人   如果是賞善大使張三,他會賞賜好人,不管壞人 如果是罰惡大使李四,他會懲罰壞人,不管好人
package visitor.俠客行;
 
import java.util.ArrayList;
import java.util.List;
 
public class 俠客島 {
 
  private List<掌門人> 掌門人List = new ArrayList<>();
   
  public void add掌門人(掌門人 某掌門) {
    掌門人List.add(某掌門);
  }
   
  public void 賞善罰惡(String 處理人) {
   
    if (處理人.equals("張三")) {
     
      for (掌門人 某掌門X : 掌門人List) {
       
        if (某掌門X instanceof 沒做過壞事的掌門) {
         
          System.out.println("好掌門, 張三: 賞賜沒做過壞事的掌門");
         
        } else if (某掌門X instanceof 做過壞事的掌門) {
         
          System.out.println("壞掌門, 張三: 不管做過壞事的掌門");
        }
         
        System.out.println();
      }
    } else if (處理人.equals("李四")) {
     
      for (掌門人 某掌門X : 掌門人List) {
       
        if (某掌門X instanceof 沒做過壞事的掌門) {
         
          System.out.println("好掌門, 李四: 不管沒做過壞事的掌門");
         
        } else if (某掌門X instanceof 做過壞事的掌門) {
         
          System.out.println("壞掌門, 李四: 懲罰做過壞事的掌門");
        }
        System.out.println();
    }
  }
  }
}
 

 

測試代碼 image_5c2481f3_37bc   上面的測試代碼中,我們創造了俠客島的“賞善罰惡二使” 並且將幾個“掌門人”交於他們處理 列印結果分別展示了對於這幾個“掌門人”,張三和李四的不同來訪,產生的不同結果   如果我們想增加來訪者怎麼辦?比如這次是龍木島主親自出島處理,好人賞賜,壞人直接處理怎麼辦? 我們可以直接新增賞善罰惡方法的處理邏輯,如下圖所示,新增加了一個else if 可以通過測試代碼看到結果 image_5c2481f3_7af6   如果有些掌門人既沒有做什麼好事,也沒有做什麼壞事怎麼處理?也就是新增一種掌門人? 你會發現,所有的判斷的地方,也還是都需要新增加一個else if ...... ̄□ ̄|| 因為上面的示例,使用的是兩層判斷邏輯,每一層都跟具體的類型有關係!!! 不管是增加新的來訪者,還是增加新的種類的成員,都不符合開閉原則,而且判斷邏輯複雜混亂   上面的過程在程式世界中, 也會經常出現。 實際開發中,經常用到集合框架 集合框架中也經常會保存不同的類型(此處指的是不同的最終類型,如果抬杠,還不都是Object    ̄□ ̄||) 比如多個不同的子類,像上面示例中的好掌門和壞掌門,都是掌門人類型,但是具體子類型不同。 對於集合中的元素,可能會有不同的處理操作 比如上面示例中的,張三和李四的到來,處理肯定不一樣,沒乾過壞事的和乾過壞事的處理也不一樣 比如去體檢,不同的項目的醫生會有不同的行為操作,你和跟你一起排隊體檢的人也不一樣,但是你還是你,他還是他     在上面的《俠客行》的示例中,我們使用了雙重判斷來確定下麵兩層問題: 一層是來訪者是誰? 另外一層是當前的掌門人是什麼類型? 如果有X種來訪者,Y種類型掌門人,怕是要搞出來X*Y種組合了,所以才會邏輯複雜,擴展性差 所以,那麼根本問題就是靈活的確定這兩個維度,來訪者和當前類型,進而確定具體的行為,對吧?   再回頭審視一下《俠客行》的示例,對於訪問者,有張三、李四、龍木島主,還可能會有其他人, 顯然,我們應該嘗試將訪問者進行抽象,張三,李四,龍木島主,他們都是具體的訪問者。 而且,而且,而且,他們都會訪問不同類型的掌門人,既然是訪問  不同類型掌門人 也就是方法名一樣,類型不一樣? 這不就是方法重載

新版代碼示例

掌門人相關角色不變
package visitor.新版俠客行;
public interface 掌門人 {
}

package visitor.新版俠客行;
public class 沒做過壞事的掌門 implements 掌門人 {
}


package visitor.新版俠客行;
public class 做過壞事的掌門 implements 掌門人 {
}
新增加訪問者角色,訪問者既可能訪問好人,也可能訪問壞人,使用方法的重載在解決  方法都是拜訪,有兩種類型的重載版本
package visitor.新版俠客行;
public interface 訪問使者 {
  void 拜訪(做過壞事的掌門 壞人);
  void 拜訪(沒做過壞事的掌門 好人);
}
張三負責賞善,當他訪問到好人時,賞賜,壞人不處理
package visitor.新版俠客行;
 
public class 張三 implements 訪問使者 {
    @Override
    public void 拜訪(沒做過壞事的掌門 好人) {
        System.out.println("好掌門, 張三: 賞賜沒做過壞事的掌門");
    }
     
    @Override
    public void 拜訪(做過壞事的掌門 壞人) {
        System.out.println("壞掌門, 張三: 不管做過壞事的掌門");
    }
}
李四負責罰惡,訪問到好人時不處理,遇到壞人時,就懲罰!
package visitor.新版俠客行;
 
public class 李四 implements 訪問使者 {
 
    @Override
    public void 拜訪(沒做過壞事的掌門 好人) {
        System.out.println("好掌門, 李四: 不管沒做過壞事的掌門");
    }

    @Override
    public void 拜訪(做過壞事的掌門 壞人) {
        System.out.println("壞掌門, 李四: 懲罰做過壞事的掌門");
    }
}
引入了訪問使者角色,我們就不需要對使者進行判斷了 藉助了使者的多態性,不管是何種使者都有訪問不同類型掌門人的方法 所以可以去掉了一層邏輯判斷,代碼簡化如下
package visitor.新版俠客行;
 
import java.util.ArrayList;
import java.util.List;
 
public class 俠客島 {
  private List<掌門人> 掌門人List = new ArrayList<>();
   
  public void add掌門人(掌門人 某掌門) {
    掌門人List.add(某掌門);
  }
   
  public void 賞善罰惡(訪問使者 使者) {
      for (掌門人 某掌門X : 掌門人List) {
         if (某掌門X instanceof 沒做過壞事的掌門) {
             使者.拜訪((沒做過壞事的掌門)某掌門X);
         } else if (某掌門X instanceof 做過壞事的掌門) {
             使者.拜訪((做過壞事的掌門)某掌門X);
         }
         System.out.println();
      }
  }
}
測試代碼也稍作調整 定義了兩個訪問者,傳遞給“賞善罰惡”方法
package visitor.新版俠客行;
 
public class Test {
 
public static void main(String[] args){
 
    俠客島 善善罰惡二使 = new 俠客島();
     
    善善罰惡二使.add掌門人(new 做過壞事的掌門());
    善善罰惡二使.add掌門人(new 沒做過壞事的掌門());
    善善罰惡二使.add掌門人(new 沒做過壞事的掌門());
    善善罰惡二使.add掌門人(new 做過壞事的掌門());
     
    訪問使者 張三 = new 張三();
    訪問使者 李四 = new 李四();
     
    善善罰惡二使.賞善罰惡(李四);
    善善罰惡二使.賞善罰惡(張三);
    }
}

 

image_5c2481f3_6b9   可以看到,《新版俠客行》和老版本的功能的一樣的,但是代碼簡化了 而且,最重要的是能夠很方便的擴展使者,比如我們仍舊增加“龍木島主”這一訪客。
package visitor.新版俠客行;
public class 龍木島主 implements 訪問使者 {
    @Override
    public void 拜訪(做過壞事的掌門 壞人) {
        System.out.println("龍木島主,懲罰壞人");
    }
    @Override
    public void 拜訪(沒做過壞事的掌門 好人) {
        System.out.println("龍木島主,賞賜好人");
    }
}
新增加了"龍木島主“訪客後,客戶端可以直接使用了,不需要修改”俠客島“的代碼了 測試代碼增加如下兩行,查看下麵結果 image_5c2481f3_110a   但是如果增加新的掌門人類型呢? 因為我們仍舊有具體類型的判斷,如下圖所示 image_5c2481f3_1f7e 所以,想要增加新的掌門人,又完蛋了   ̄□ ̄||   而且,現在的判斷邏輯也還是交織著,複雜的。 對於訪問者的判斷,我們藉助於多態以及方法的重載,去掉了一層訪問者的判斷 通過多態可以將請求路由到真實的來訪者,通過方法重載,可以調用到正確的方法   如果能把這一層的if else if判斷也去掉,是不是就可以靈活擴展掌門人了呢? 使者只知道某掌門X,但是他最終的具體類型,是不知道的 所以,沒辦法直接調用拜訪方法的,因為我們的確沒有這種參數類型的方法  image_5c2481f3_959 ps:有人覺得“拜訪”方法的類型使用 掌門人  不就好了麽 但是對於不同的具體類型有不同的行為,那你在“拜訪”方法中還是少不了要進行判斷,只是此處判斷還是“拜訪”方法內判斷的問題)   前面的那段if else if判斷邏輯,訪問的方法都是  使者.拜訪,只不過具體類型不同 image_5c2481f4_73d5 但是如何確定類型?問題也就轉換為”到底怎麼判斷某掌門X的類型“或者”到底誰知道某掌門X的類型“ 那誰知道他的類型呢? 如果不藉助外力,比如 instanceof 判斷的話,還有誰知道?某掌門X 他自己知道!!!他自己知道!!! 所以,如果是在  某掌門X自己內部的方法,就可以獲取到this了,這就是當前對象的真實類型 把這個類型在回傳給來訪使者不就可以了麽 所以 給掌門人定義一個“接受拜訪”方法,不管何種類型的掌門人,都能夠接受各種訪客的拜訪 接受拜訪(訪問使者 賞善罰惡使者){ 賞善罰惡使者.拜訪(this);  

最新版俠客行代碼示例

說起來有點迷惑,我看看代碼 《最新版俠客行》 掌門人都增加了”接受拜訪“的方法
package visitor.最新版本俠客行;
public interface 掌門人 {
void 接受拜訪(訪問使者 賞善使者);
}

 

package visitor.最新版本俠客行;
public class 沒做過壞事的掌門 implements 掌門人 {
  @Override
  public void 接受拜訪(訪問使者 賞善罰惡使者) {
    賞善罰惡使者.拜訪(this);
  }
}

 

package visitor.最新版本俠客行;
public class 做過壞事的掌門 implements 掌門人 {
  @Override
  public void 接受拜訪(訪問使者 賞善罰惡使者) {
    賞善罰惡使者.拜訪(this);
  }

}
訪問使者相關角色與《新版俠客行》中一樣
package visitor.最新版本俠客行;
public interface 訪問使者 {
    void 拜訪(做過壞事的掌門 壞人);
    void 拜訪(沒做過壞事的掌門 好人);
}
 
 
package visitor.最新版本俠客行;
public class 張三 implements 訪問使者 {
    @Override
    public void 拜訪(沒做過壞事的掌門 好人) {
        System.out.println("好掌門, 張三: 賞賜沒做過壞事的掌門");
    }
    @Override
    public void 拜訪(做過壞事的掌門 壞人) {
        System.out.println("壞掌門, 張三: 不管做過壞事的掌門");
    }
}
 
package visitor.最新版本俠客行; public class 李四 implements 訪問使者 { @Override public void 拜訪(沒做過壞事的掌門 好人) { System.out.println("好掌門, 李四: 不管沒做過壞事的掌門"); } @Override public void 拜訪(做過壞事的掌門 壞人) { System.out.println("壞掌門, 李四: 懲罰做過壞事的掌門"); } }
此時的俠客島輕鬆了,不再需要來回的判斷類型了
package visitor.最新版本俠客行;
 
import java.util.ArrayList;
import java.util.List;
public class 俠客島 {
    private List<掌門人> 掌門人List = new ArrayList<>();
    public void add掌門人(掌門人 某掌門) {
        掌門人List.add(某掌門);
    }
    public void 賞善罰惡(訪問使者 使者) {
        for (掌門人 某掌門X : 掌門人List) {
            某掌門X.接受拜訪(使者);
            System.out.println();
        }
    }
}
image_5c2481f4_4529 從結果看跟上一個版本一樣 但是很顯然,我們的俠客島輕鬆了   接下來我們看一下新增加訪客和新增加掌門人的場景 擴展龍木島主
package visitor.最新版本俠客行;

public class 龍木島主 implements 訪問使者 {
@Override
public void 拜訪(做過壞事的掌門 壞人) {
System.out.println("龍木島主,懲罰壞人");
}

@Override
public void 拜訪(沒做過壞事的掌門 好人) {
System.out.println("龍木島主,賞賜好人");
}
}
測試代碼如下,顯然因為拜訪使者的抽象,才得以能夠更好的擴展訪問者,所以此處肯定跟《新版俠客行》一樣便於擴展 image_5c2481f4_4404   看看如果擴展一個新的掌門人
package visitor.最新版本俠客行;
public class 不好不壞的掌門 implements 掌門人 {
@Override
public void 接受拜訪(訪問使者 賞善罰惡使者) {
賞善罰惡使者.拜訪(this);
}
}
但是,”訪問使者“裡面沒有能夠拜訪”不好不壞的掌門“方法啊?怎麼辦? 只能添加唄,如下圖所示,完蛋了........ image_5c2481f4_3f58

代碼演化小結

看得出來,《最新版俠客行》解決了複雜判斷的問題,也解決了訪問者擴展的問題 但是對於被訪問者的類型的擴展,顯然是沒有擴展性的,不符合開閉原則 這一點體現出來了這種解決方法的傾向性,傾向於擴展行為,可以自如的增加新的行為 但是不能輕鬆的增加元素類型   測試代碼Test類不需要修改 看一下列印結果 image_5c2481f4_3462

最新版俠客行結構

image_5c2481f4_7425

迴首意圖

再回頭看下訪問者模式的意圖 表示一個作用於某對象結構中的各元素的操作。它使你可以在不改變各元素類的前提下定義作用於這些元素的新操作。 就是上面示例中,對於來訪者的擴展嘛   最初的動機就是處理《俠客行》中類似的問題 集合容器中保存了不同類型的對象,他們又可能有多種不同場景的操作 比如一份名單,班長可能拿過去收作業,班主任拿過去可能點名 名單裡面都有你也有他,你就是那個你,他還是那個他,但是你的作業是你的作業,他的作業是他的作業。 所以對於班長和班主任兩個訪問者,同學們的行為是不一樣的,對同一來訪者,不同的同學的行為又是不一樣的

結構

image_5c2481f4_1c63   抽象元素角色Element 抽象元素一般是抽象類或者介面 通常它定義一個 accept(抽象訪問者) 方法,用來將自身傳遞給訪問者 具體的元素角色ConcreateElement 具體元素實現了accept方法,在accept方法中調用訪問者的訪問方法以便完成對一個元素的操作 抽象訪問者Visitor 定義一個或者多個訪問操作 抽象訪問者需要面向具體的被訪問者元素類型,所以有幾個具體的元素類型需要被訪問,就有幾個重載方法 具體的訪問者ConcreateVisitor 具體的訪問者封裝了不同訪問者,不同類型對象的具體行為,也就是最終的分情況的處理邏輯 對象結構ObjectStructure 對象結構是元素的集合,用於存放元素的對象,並且一般提供遍歷內部元素的方法 客戶端角色Client 組織被訪問者,然後通過訪問者訪問   訪問者模式有兩個主要層次,訪問者以及被訪問元素 訪問者有不同的類型,被訪問元素有不同的類型 每一種訪問者對於每一種被訪問元素都有一種不同的行為,這不同的行為是封裝在訪問者的方法中 所以訪問者需要進行訪問方法visit的重載,被訪問元素有幾種類型,就有幾種重載版本 面向細節的邏輯既然被封裝在訪問者中,被訪問元素就不需要面向細節了,只需要把自己的類型傳遞給訪問者即可 所以,所有的被訪問元素都只有一個版本的accept方法

概念示例代碼

我們可以抽象化的看下下麵的例子 下麵的代碼很簡單,A有三種子類型,B有三種子類型 不同的A和不同的B,將會擦出不一樣的火花,也就是會出現9種可能的場景 將A定義為訪問者,那麼A就要藉助方法的重載實現不同類型被訪問者B的不同行為 而將方法的調用轉變為被訪問者的反向調用----this傳遞給訪問者
package visitor;
 
public class example {
public static void main(String[] args) {
 
A1 a1 = new A1();
A2 a2 = new A2();
A3 a3 = new A3();
 
B1 b1 = new B1();
B2 b2 = new B2();
B3 b3 = new B3();
 
b1.accept(a1);
b1.accept(a2);
b1.accept(a3);
b2.accept(a1);
b2.accept(a2);
b2.accept(a3);
b3.accept(a1);
b3.accept(a2);
b3.accept(a3);
}
}
 
 
abstract class A {
 
abstract void visit(B1 b1);
abstract void visit(B2 b2);
abstract void visit(B3 b3);
}
 
class A1 extends A {
@Override
void visit(B1 b1) {
System.out.println("A1 play with B1");
}
 
@Override
void visit(B2 b2) {
System.out.println("A1 play with B2");
}
 
@Override
void visit(B3 b3) {
System.out.println("A1 play with B3");
}
}
 
class A2 extends A {
@Override
void visit(B1 b1) {
System.out.println("A2 play with B1");
}
 
@Override
void visit(B2 b2) {
System.out.println("A2 play with B2");
}
 
@Override
void visit(B3 b3) {
System.out.println("A2 play with B3");
}
}
 
class A3 extends A {
@Override
void visit(B1 b1) {
System.out.println("A3 play with B1");
}
 
@Override
void visit(B2 b2) {
System.out.println("A3 play with B2");
}
 
@Override
void visit(B3 b3) {
System.out.println("A3 play with B3");
}
}
 
 
abstract class B {
abstract void accept(A a);
}
 
class B1 extends B {
@Override
void accept(A a) {
a.visit(this);
}
}
 
class B2 extends B {
@Override
void accept(A a) {
a.visit(this);
}
}
 
class B3 extends B {
@Override
void accept(A a) {
a.visit(this);
}
}

 

 

image_5c2481f4_6086 這種重載和回傳自身的形式,完全可以當作一個套路來使用,對於這種組合形式的場景,非常受用。 訪問者的自身藉助多態特性,又依賴方法重載,然後再藉助於this回傳達到反向確定類型調用,真心精巧。

總結

訪問者模式靈活的處理了不同類型的元素,面對不同的訪問者,有不同的行為的場景。 這種組合場景,判斷邏輯複雜繁瑣,訪問者模式可以做到靈活的擴展增加更多的行為,而不需要改變原來的類。 訪問者模式傾向於擴展元素的行為,當擴展元素行為時,滿足開閉原則 但是對於擴展新的元素類型時,將會產生巨大的改動,每一個訪問者都需要變動,所以在使用訪問者模式是要考慮清楚元素類型的變化可能。 因為訪問者依賴的是具體的元素,而不是抽象元素,所以才難以擴展   訪問者依賴的是具體元素,而不是抽象元素,這破壞了依賴倒置原則,特別是在面向對 象的編程中,拋棄了對介面的依賴,而直接依賴實現類,擴展比較難。   當業務規則需要遍歷多個不同的對象時,而且不同的對象在不同的場景下又有不同的行為 你就應該考慮使用訪問者模式 如果對象結構中的對象不常變化,但是他們的行為卻經常變化時,也可以考慮使用,訪問者模式可以很靈活的擴展新的訪客。 原文地址:訪問者模式 Visitor 行為型 設計模式(二十七)    
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • CSS 是 Cascading Style Sheets的簡稱,中文稱為層疊樣式表,用來控制網頁數據的表現,可以使網頁的表現與數據內容分離。 css 有四種引入方式 一,行內式: 二, 嵌入式 嵌入式是將CSS樣式集中寫在網頁的標簽對的標簽對中。 三, 鏈接式 將一個.css文件引入到HTML文件中 ...
  • 解釋器模式的定義 定義: 給定一門語言,定義它的文法的一種表示, 並定義一個解釋器, 該解釋器使用該表示來解釋語言中的句子. 其類圖如下: 其中的角色說明: 抽象表達式代碼: 抽象表達式通常只有一個方法, 抽象表達式是生成語法集合的關鍵, 每個語法集合完成指定語法解析任務, 它是通過遞歸調用的方式, ...
  • 備忘錄模式的定義 定義: 在不破壞封裝性的前提下, 捕獲一個對象的內部狀態, 併在該對象之外保存這個狀態. 這樣以後就可將該對象回覆到原先保存的狀態 通俗的說, 就是記錄下類的當前狀態, 當需要的時候恢復 類圖如下: 其中各角色如下: 發起人角色代碼: 備忘錄角色代碼: 備忘錄管理員角色代碼: 場景 ...
  • 訪問者模式的定義 定義: 封裝一些作用於某種數據結構中的各元素的操作, 它可以在不改變數據結構的前提下定義作用於這些元素的新的操作 通俗的說, 就是定義一個訪問者角色, 當對指定角色進行訪問時要通過訪問者進行訪問 其類圖如下: 各角色說明: 抽象元素代碼: 具體元素代碼: 抽象訪問者代碼: 具體訪問 ...
  • 觀察者模式的定義 定義: 定義對象間一種一對多的依賴關係,使得每當一個對象改變狀態,則所有依賴於它的對象都會得到通知並被自動更新 通俗的說, 就是一個類的某個條件滿足時, 會調用一系列定義好的方法 其類圖如下: 其中的4個角色: 被觀察者抽象類代碼: 具體被觀察者代碼: 觀察者介面代碼: 具體的觀察 ...
  • 責任鏈模式通過為請求創建一個 接收者對象的鏈,對請求的發送者和接收者進行解耦。 介紹 責任鏈屬於行為型模式,在這種模式中,通常每個接收者都包含對另一個接收者的引用,如果一個對象不能處理該請求,那麼則會繼續往下傳遞,依此類推。可以參考 C 中的事件處理程式就是採用這種思想。 類圖描述 代碼實現 1、創 ...
  • 命令模式是一種數據驅動型的設計模式,它以命令的形式包裹在對象中,並傳遞給調用者。 介紹 命令模式屬於行為型設計模式,它通過將一個請求封裝成一個對象,從而使我們可以用不同的請求對客戶端進行參數化。 類圖描述 代碼實現 1、創建命令介面 2、創建一個模擬請求類 3、創建命令介面的實現類 4、創建命令調用 ...
  • 在工廠模式中,我們沒有創建邏輯暴露給客戶端創建對象,並使用一個通用的介面引用新創建的對象。 1.創建Shape介面 2.創建多個Shape實現類(這裡創建了3個) 3.創建工廠 4.使用工廠通過傳遞類型等信息來獲取具體類的對象 5.輸出結果如下: ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...