今日はオブジェクト指向設計について考えていましたが、ifステートメントを避けるべきかどうか疑問に思いました。私の考えでは、ifステートメントが必要な場合は、同じメソッドを実装する2つのオブジェクトを簡単に作成できると思います。 2つのメソッドの実装は、元のifステートメントの2つの可能な分岐にすぎません。
これは極端に思えるかもしれませんが、ある程度は議論の余地があるようです。これについて何か考えはありますか?
[〜#〜]編集[〜#〜]
時間がかかりませんでした。これは極端すぎると思います。ただし、OOPの下では、ifステートメントの方がはるかに少ないはずです。
2番目の編集
これについてはどうですか:その属性に基づいてメソッドの実装を決定するオブジェクト。つまり、2つの方法でsomeMethod()
を実装し、いくつかの制限を指定できます。どの時点でも、オブジェクトはそのプロパティに基づいて正しいメソッド実装にルーティングされます。したがって、if(x > 5)
の場合、x
属性に依存する2つのメソッドのみがあります。
一つだけ言えることがあります。人々が何を言っても、不要な分岐を単純化して排除することを考えることは、ソフトウェア開発者として成熟していることを示しています。分岐が悪い理由、テスト、メンテナンス、バグの発生率の高さなど、多くの理由があります。これは、私が人々にインタビューするときに探すことの1つであり、開発者としての成熟度を示す優れた指標です。より少ない条件を使用して、実験を続け、コードと設計を簡素化することをお勧めします。この切り替えを行ったところ、コードのデバッグに費やす時間が大幅に短縮され、簡単に機能しました。その後、何かを変更する必要がある場合、ほとんどのコードがシーケンシャルであるため、変更は非常に簡単でした。繰り返しますが、他の人の発言に関係なく、あなたがしていることを100%続けることをお勧めします。ほとんどの開発者ははるかに低いレベルで作業し、考えており、ルールに従っていることを覚えておいてください。これをうまくやるのに良い仕事を。
Ifステートメントまたは3値ロジックなしで以下を実装する方法を説明します。
if ( x < 5 ) {
x = 0
} else {
print x;
}
確かに、多くの場合、複雑な条件文は多態性で簡略化できることは事実です。しかし、それは常に役立つわけではありません。 Fowlerのリファクタリングの本を読んで、いつになるかを理解してください。
http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html
Ifステートメントを完全に排除することは現実的ではなく、私は Ori が示唆していることだとは思いません。しかし、それらは多くの場合、多態性を使用して置き換えることができます。 (そして多くのステートメントを切り替えることができます)。
Francesco Cirilloは、この問題の認識を高めるために Anti-Ifキャンペーン を開始しました。彼は言う:
オブジェクトの使用方法を知ることで、開発者はタイプに基づいてIFを排除できます。これは、ソフトウェアの柔軟性と進化する能力を最も頻繁に損なうものです。
あなたまたはあなたのチームは キャンペーンに参加する もできます。
私の先生の一人がよく言っていました。私はそのようなことについて非常に独断的である人々は通常生計のためにプログラムしないと思う傾向があります。
Ifステートメントの回避:実行する方法はたくさんありますが、そのうちの1つを以下に示します。
int i=0;
if(i==1)
{
//Statement1
}
if(i==2)
{
//Statement2
}
if(i==3)
{
//Statement3
}
if(i==4)
{
//Statement4
}
辞書とデリゲートを使用する:
delegate void GetStatement ();
Dictionary<int,GetStatement > valuesDic=new Dictionary<int,GetStatement >();
void GetStatement1()
{
//Statement1
}
void GetStatement2()
{
//Statement2
}
void GetStatement3()
{
//Statement3
}
void GetStatement4()
{
//Statement4
}
void LoadValues()
{
valuesDic.Add(1,GetStatement1);
valuesDic.Add(2,GetStatement2);
valuesDic.Add(3,GetStatement3);
valuesDic.Add(4,GetStatement4);
}
Ifステートメントの置き換え:
int i=0;
valuesDic[i].Invoke();
Anti-Ifキャンペーン を見てください。アイデアは、アプリケーション内のすべてのものを戦略または状態パターンに置き換えることではありません。特に、列挙型などに基づく複雑な分岐ロジックがある場合、戦略パターンにリファクタリングする必要があるという考えです。
その場合は、ファクトリーを使用してifをまとめて削除できます。これは比較的簡単な例です。もちろん、実際のケースで述べたように、戦略のロジックは単に「I's Active」を出力するよりも少し複雑です。
public enum WorkflowState
{
Ready,
Active,
Complete
}
public interface IWorkflowStrategy
{
void Execute();
}
public class ActiveWorkflowStrategy:IWorkflowStrategy
{
public void Execute()
{
Console.WriteLine("The Workflow is Active");
}
}
public class ReadyWorkflowStrategy:IWorkflowStrategy
{
public void Execute()
{
Console.WriteLine("The Workflow is Ready");
}
}
public class CompleteWorkflowStrategy:IWorkflowStrategy
{
public void Execute()
{
Console.WriteLine("The Workflow is Complete");
}
}
public class WorkflowStrategyFactory
{
private static Dictionary<WorkflowState, IWorkflowStrategy> _Strategies=
new Dictionary<WorkflowState, IWorkflowStrategy>();
public WorkflowStrategyFactory()
{
_Strategies[WorkflowState.Ready]=new ReadyWorkflowStrategy();
_Strategies[WorkflowState.Active]= new ActiveWorkflowStrategy();
_Strategies[WorkflowState.Complete] = new CompleteWorkflowStrategy();
}
public IWorkflowStrategy GetStrategy(WorkflowState state)
{
return _Strategies[state];
}
}
public class Workflow
{
public Workflow(WorkflowState state)
{
CurrentState = state;
}
public WorkflowState CurrentState { get; set; }
}
public class WorkflowEngine
{
static void Main(string[] args)
{
var factory = new WorkflowStrategyFactory();
var workflows =
new List<Workflow>
{
new Workflow(WorkflowState.Active),
new Workflow(WorkflowState.Complete),
new Workflow(WorkflowState.Ready)
};
foreach (var workflow in workflows)
{
factory.GetStrategy(workflow.CurrentState).
Execute();
}
}
}
ある意味で、これは良い考えです。代わりに仮想関数を使用できる場合、オブジェクト内の型フィールドを切り替えることは通常、悪い考えです。しかし、仮想関数メカニズムは、一般的にif()テストを置き換えることを意図したものではありません。
元のステートメントが何を比較しているかによって異なります。私の経験則では、それがswitch
またはif
であり、列挙型と等しいかどうかをテストしている場合は、別のメソッドの候補として適しています。ただし、switch
およびif
ステートメントは、他の多くの種類のテストで使用されます。関係演算子(<
、>
、 <=
、>=
)を特殊なメソッドで使用すると、いくつかの種類の列挙型テストが標準のステートメントでより適切に機能します。
したがって、if
sが次のようになっている場合にのみ置き換える必要があります。
if (obj.Name == "foo" || obj.Name == "bar") { obj.DoSomething(); }
else if (obj.Name == "baz") { obj.DoSomethingElse(); }
else { obj.DoDefault(); }
IfTrueの質問に答えて:
まあ、もしあなたがオープンクラスと十分に強力な依存型システムを持っているなら、少しばかげているとしてもそれは簡単です。非公式かつ特定の言語ではない:
class Nat {
def cond = {
print this;
return this;
}
}
class NatLessThan<5:Nat> { // subclass of Nat
override cond = {
return 0;
}
}
x = x.cond();
(続き...)
または、オープンクラスはありませんが、複数のディスパッチクラスと匿名クラスを想定しています。
class MyCondFunctor {
function branch(Nat n) {
print n;
return n;
}
function branch(n:NatLessThan<5:Nat>) {
return 0;
}
}
x = new MyCondFunctor.branch(x);
または、以前と同様ですが、匿名クラスを使用します。
x = new {
function branch(Nat n) {
print n;
return n;
}
function branch(n:NatLessThan<5:Nat>) {
return 0;
}
}.branch(x);
もちろん、そのロジックをリファクタリングすれば、はるかに簡単な時間になるでしょう。完全にチューリング完全な型システムが存在することを覚えておいてください。
Ifステートメントなしで使用するオブジェクトのメソッドをどのように決定しますか?
Assume we have conditional values.
public void testMe(int i){
if(i=1){
somevalue=value1;
}
if(i=2){
somevalue=value2;
}
if(i=3){
somevalue=value3;
}
}
//**$$$$$you can replace the boring IF blocks with Map.$$$$$**
// ================================================ ============
Same method would look like this:
--------------------------------
public void testMe(int i){
Map<Integer,String> map = new HashMap<Integer,String>();
map.put(1,value1);
map.put(2,value2);
map.put(3,value3);
}
This will avoid the complicated if conditions.
クラスのロードにファクトリパターンを使用する場合は、同様のソリューションを使用できます。
public void loadAnimalsKingdom(String animalKingdomType)
if(animalKingdomType="bird"){
Bird b = new Bird();
}
if(animalKingdomType="animal"){
Animal a= new Animal();
}
if(animalKingdomType="reptile"){
Reptile r= new Reptile();
}
}
public void loadAnimalsKingdom(String animalKingdomType)
{
Map <String,String> map = new HashMap<String,String>();
map.put("bird","com.animalworld.Bird.Class");
map.put("animal","com.animalworld.Animal.Class");
map.put("reptile","com.animalworld.Reptile.Class");
map.get(animalKingdomType);
***Use class loader to load the classes on demand once you extract the required class from the map.***
}
ソリューションが好きですか?高く評価してください。-Vv
else
のまったく新しいクラスを作成すると、技術的には実行可能ですが、コードの読み取り、保守、または正確性の証明が困難になる可能性があります。
それは興味深いアイデアです。私はあなたがcouldを理論的に行うと思いますが、それをサポートするように特別に設計されていない言語では、それは非常に苦痛になるでしょう。確かに理由はありません。
彼が言っていること、または彼が言っていることは、「タグ付け」の乱用を回避し、オブジェクトをサブクラス化するか、オブジェクトを再考するほうが理にかなっている場合は、複数のifステートメントによってカスタム機能をクラスに追加するのが最善であると考えていることだと思います。階層。
すべてのifステートメントのアイデアにその議論を適用することはかなり極端だと思いますが、一部の言語では、特定のシナリオでそのアイデアを適用する機能を提供しています。
これはサンプルですPython実装は過去に固定サイズの両端キュー(両端キュー)に対して記述しました。 "remove"メソッドを作成して、その中にifステートメントを作成する代わりに、リストがいっぱいかどうかは、2つのメソッドを作成し、必要に応じてそれらを「削除」関数に再割り当てするだけです。
次の例では、「remove」メソッドのみをリストしていますが、「append」メソッドなども当然あります。
class StaticDeque(collections.deque):
def __init__(self, maxSize):
collections.deque.__init__(self)
self._maxSize = int(maxSize)
self._setNotFull()
def _setFull(self):
self._full = True
self.remove = self._full_remove
def _setNotFull(self):
self._full = False
self.remove = self._not_full_remove
def _not_full_remove(self,value):
collections.deque.remove(self,value)
def _full_remove(self,value):
collections.deque.remove(self,value)
if len(self) != self._maxSize and self._full:
self._setNotFull()
ほとんどの場合、それはそれほど有用ではありませんが、役立つ場合もあります。
IFは条件付きの複雑さを増し、可能な限り回避する必要があるため、IFは適切ではないことをヴァンスは同意します。
ポリモーフィズムは、意味をなすために使用される条件で完全に実行可能なソリューションであり、「回避しない」ことではありません。
OOP要件には適合しませんが、データ指向のアプローチは 分岐を避ける になる傾向があります。
答えは漠然とはいっぽいです。特に、言語が何らかの強力な関数型プログラミング(C#、F#、OCamlなど)を許可している場合。
2つのifステートメントを含むコンポーネントは、2つのビジネスルールを強く結合するため、分割します。
非常に一般的な経験則としてそれを採用しますが、私は同意します。 ifステートメントがたくさんある場合は、別のアプローチについて考える必要があります。
Ifステートメントはプログラミングの中核となるので、要するに、賢明にそれらを回避することはできません。
ただし、OOPの主要な目標は、実際には「柱」の1つですが、カプセル化です。古い「変化するものをカプセル化する」ルールは、オブジェクトのすべてのオプションを説明しようとしている問題のあるifおよびcaseステートメントを削除するのに役立ちます。ブランチや特別なケースなどを処理するためのより良い解決策は、「ファクトリー」設計パターン(もちろん、必要に応じて、抽象ファクトリーまたはファクトリーメソッド)のようなものを使用することです。
たとえば、メインコードループでifステートメントを使用して使用しているOSをチェックし、分岐して各OSに異なるオプションを持つGUIウィンドウを作成するのではなく、メインコードはファクトリからオブジェクトを作成し、OSを使用してどのOSを決定します固有の具体的なオブジェクトを作成します。これを行うことで、メインコードループからバリエーション(および長いif-then-else句)を取り出し、子オブジェクトに処理を任せることになります。そのため、次回、新しいOSのサポートなどの変更を行う必要があります。 、ファクトリインターフェースから新しいクラスを追加するだけです。
私は最近、反IFの話に従ってきましたが、それは私には極端/双曲的レトリックのように聞こえます。しかし、私はこのステートメントに真実があると思います:ifステートメントのロジックは、ポリモーフィズムによってより適切に実装できることがよくあります。 ifステートメントを修正するたびにそのことを念頭に置いてください。そうは言っても、ifステートメントは依然としてコアロジック構造であり、信条として恐れたり回避したりすべきではありません。
何を理解する必要があります(x > 5)
本当に意味。 x
が数値を表すとすると、基本的には5より大きいすべての数値を「分類」します。したがって、コードはpython構文の言語で次のようになります。
class Number(Object):
# ... Number implementation code ... #
def doSomething():
self = 0
return self
def doSomethingElse():
pass
class GreaterThan5(Number):
def doSomething():
print "I am " + self
def doSomethingElse():
print "I like turtles!"
次に、次のようなコードを実行します。
>>> type(3)
<class Number>
>>> type(3+3)
<class GreaterThan5>
>>> 3.doSomething()
0
>>> (3 + 3).doSomething()
I am 6
>>> (7 - 3).doSomethingElse()
>>>
ここでの自動型変換は重要です。私の知る限り、今日のどの言語でも、これほど整数をいじることはできません。
結局、あなたはあなたのコードで何でもすることができます。それを読んでいる人がすぐに理解できる限り。したがって、整数または異常なものに対するポリモーフィックなディスパッチは、その背後に本当に正当な理由があるはずです。
オブジェクト指向のアプローチについて私が理解していることの2つのビット-
まず、プログラム内のどのオブジェクトが直感的である必要があります。つまり、数学関数を提供する「算術」クラスを作成しようとするべきではありません。これはOODの乱用です。
第二に、これは私の非常に強い意見です。オブジェクト指向デザインではなく、オブジェクトandメソッド指向デザインと呼ぶべきです!オブジェクトのメソッド名自体が直感的でない場合、継承されたオブジェクトは、すでに利用可能なメソッドを再実装することになります。
私によると、オブジェクト指向のアプローチは、手続き型のアプローチに代わるものではありません。むしろ、それは言語の作成者にとって主に2つの主な理由のためです-
変数のスコープの機能が向上しました。
グローバル変数が多すぎるのではなく、ガベージコレクションの機能が向上しました。
かなり極端です。あなたが提案していることを行うと、関数全体が完全に異なる場合を除き、多くの不必要なコードの重複が発生します。もしそうなら、おそらくそれはメソッド呼び出しの反対側にあるはずです。
Ifステートメントは、確かにオブジェクト指向の設計でその位置を占めています。
確かに、あなたが何をしているかに関係なく、何らかの比較を行う必要がありますか?結局... ifステートメントを避けることはできますが、ifステートメントを使用してコードと同一のコードを生成することになります。
誰かが私が間違っている場合は私を修正しますが、あなたがこれを実行して勝利フォームを得ることができる時期は考えられません。