七:リ氏置換の原則


何が里氏の置換の原則ですか
リッツ置換の原則の厳密な表現は、T 1の各タイプのオブジェクトo 1に対して、T 2のタイプのオブジェクトo 2がある、T 1で定義されたすべてのプログラムPがすべてのオブジェクトo 1でo 2に置換とき、プログラムPの挙動が変化しないように、タイプT 2がタイプT 1のサブタイプである.
言い換えれば、1つのソフトウェアの乳幼児が使用する量のベースクラスであれば、必ずそのサブクラスが適用され、ベースクラスオブジェクトとサブクラスオブジェクトの違いはまったく気づかない.逆に成立しない:もし1つのソフトウェアエンティティがサブクラスを使用するならば、それは必ずしもベースクラスに適用されるとは限らない.
二:Java言語によるリ氏置換のサポート
コンパイルの時期、Java言語コンパイラは1つのプログラムがリッジ置換に合致するかどうかを検査し、リッジ置換はベースタイプが使用する場所であれば、サブタイプが必ず適用されるため、サブクラスはベースクラスのすべてのインタフェースを備えなければならない.たとえば、ベースクラスBaseはpublicメソッドmethod()を宣言します.サブクラスsubは、このメソッドのアクセス権をpublicからpackageまたはprivateに変更できますか?リ氏置換の観点からこの問題を考察すると、答えは難しくない.クライアントはBaseの公開方法を呼び出す可能性が完全にあるからだ.サブクラスに取って代わると、この方法はプライベートになり、クライアントは呼び出すことができない.明らかにこれはリ氏置換の法則に違反している.Javaコンパイラはこのようなプログラムを通過させることはできない.Java言語のリ氏置換サポートの限界:Javaコンパイラは1つのシステムが実現と商業論理の上でリ氏置換法則を満たすかどうかを検査することができなくて、1つの有名な例は“正方形類が長方形類の子類であるかどうか”の問題です!!
三:里氏置換原則の設計モデルにおける体現
A:ポリシーモード:ポリシーモードは、「アルゴリズムのセットがある場合は、各アルゴリズムをカプセル化して交換できるようにする」ということです.パッケージの概念は理解に難くなく、すべてのアルゴリズムを交換できるようにするには、すべての具体的なポリシーロールを1つのタイプの階層構造に配置し、共通のインタフェースを持つ必要があります.この交換依存性は、AbstractStrategy=new ConcreteStratetyA()を遵守することであることは明らかです.このコードから、クライアントはベースクラスタイプに依存し、変数の実際のタイプは特定のポリシーであり、これは個々のポリシーロールが「プラグ・アンド・プレイ」できる鍵であることがわかります.
B:合成モード
C:エージェントモード、エージェントモードは1つのオブジェクトに1つのエージェントオブジェクトを提供し、エージェントオブジェクトによって元のオブジェクトへの参照を制御する.エージェントモードが成立できる鍵はエージェントモードと実際のモードが抽象的なテーマの役割のサブクラスであり、クライアントは抽象的なテーマしか知らないが、エージェントテーマは抽象的なテーマに取って代わって必要な場所に現れることができる.真実のテーマを舞台裏に隠す.
四:コード再構築の観点から理解する
リ氏置換は基類と子類の関係を言い、このような関係が存在する場合にのみリ氏置換関係が存在する.2つの特定のクラスAとBの関係がリッツ置換の原則の設計に違反した場合、具体的な状況に応じて以下の2つの方法でコード再構成を行うことができる.
(1)新たな抽象クラスCを2つのクラスの具体的なスーパークラスとして作成する、AとBの共通の行為をCに移動して、AとBの行為が完全に一致しないという問題を解決する.
(2)AとBの継承関係を委任関係に書き換える.
五:正方形は長方形のサブクラスとして使用できません
class Rectangle{ 
private long width; 
private long height; 
public long getWidth() { 
return width; 
} 
public void setWidth(long width) { 
this.width = width; 
} 
public long getHeight() { 
return height; 
} 
public void setHeight(long height) { 
this.height = height; 
} 
} 
class Square extends Rectangle{ 
private long size; 

public long getWidth() { 
return getSize(); 
} 
public void setWidth(long width) { 
setSize(width); 
} 
public long getHeight() { 
return getSize(); 
} 
public void setHeight(long height) { 
setSize(height); 
} 

public long getSize() { 
return size; 
} 

public void setSize(long size) { 
this.size = size; 
} 

} 
public class Test { 
public void resize(Rectangle r){ 
int i = 0; 
while(r.getHeight()<=r.getWidth()){ 
i++; 
r.setHeight(r.getHeight() + 1); 
if(i>100){ 
break; 
} 
System.out.println("******************************************* ************" + i); 
} 
} 
public static void main(String[] args){ 
Test t = new Test(); 
Rectangle r = new Rectangle(); 
r.setHeight(10); 
r.setWidth(20); 
Square s = new Square(); 
s.setSize(10); 
t.resize(r); 
//t.resize(s); 
} 
} 

上記の例では、resizeがベースクラスのRectangleオブジェクトに伝達する場合、このresizeメソッドは長さを超えるまで幅を増加させ続け、停止する.しかし、resizeメソッドにSquareオブジェクトが入力されると、このresizeメソッドは正方形のエッジをオーバーフローするまで増加させます.これは、親タイプのオブジェクトを子タイプのオブジェクトで置き換える場合、得られる結果が異なることを示すが、子タイプのオブジェクトを求めるには必ず親タイプのオブジェクトを置き換えることができるので、正方形は長方形の子ではない.
次のコード再構築を行う必要があります.
public interface BaseInterface { 
public long getWidth(); 
public long getHeight(); 
} 
public class MyRectangle implements BaseInterface{ 
private long width; 
private long height; 

public long getWidth() { 
return width; 
} 
public void setWidth(long width) { 
this.width = width; 
} 
public long getHeight() { 
return height; 
} 
public void setHeight(long height) { 
this.height = height; 
} 
} 
public class MySquare implements BaseInterface{ 
private long size; 
public long getSize() { 
return size; 
} 
public void setSize(long size) { 
this.size = size; 
} 
public long getHeight() { 
return getSize(); 
} 
public long getWidth() { 
return getSize(); 
} 
} 
では、リ氏の置換を破壊する問題はここでどのように避けられますか?秘密はベースクラスBaseInterfaceクラスに付与方法がないため、resizeメソッドはBaseInterfaceタイプに適用することは不可能であり、異なる具体的なサブクラスRectangleとSquareにしか使用できないため、リス置換の原則が破壊されることはない.
次のテストクラスはだめです.
public class MyTest { 
public void resize(BaseInterface r){ 
int i = 0; 
while(r.getHeight()<=r.getWidth()){ 
i++; 
r.setHeight(r.getHeight() + 1);//  ,BaseInterface            
if(i>100){ 
break; 
} 
System.out.println("******************************************* ************" + i); 
} 
} 
public static void main(String[] args){ 
MyTest t = new MyTest(); 
Rectangle r = new Rectangle(); 
r.setHeight(10); 
r.setWidth(20); 
Square s = new Square(); 
s.setSize(10); 
t.resize(r);//   
} 
} 

テストコードは次のように変更されました.
public class MyTest { 
public void resize(Rectangle r){ 
int i = 0; 
while(r.getHeight()<=r.getWidth()){ 
i++; 
r.setHeight(r.getHeight() + 1);//  ,BaseInterface            
if(i>100){ 
break; 
} 
System.out.println("******************************************* ************" + i); 
} 
} 
public static void main(String[] args){ 
MyTest t = new MyTest(); 
Rectangle r = new Rectangle(); 
r.setHeight(10); 
r.setWidth(20); 
Square s = new Square(); 
s.setSize(10); 
t.resize(s); 
} 
} 

具体的なクラスから継承するのではなく、抽象クラスから継承しなければならない.
PropertiesはHashTableのサブクラスであり、Stringタイプのkeyとvalueしか受け入れられない特殊なHashTableであることが知られているが、そのスーパークラスは任意のタイプのkeyとvalueを受け入れることができ、これはPropertiesがいつでもHashTableに取って代わることができないことを意味し、Java言語のこの設計もリバース置換の原則に違反している.