ソフトウェア工学

システム開発ライフサイクル内で働く専門家、学者、学生向けのQ&A

4
私が書くすべてのクラスはインターフェースに準拠すべきですか?
私はTypescriptでゲームを作成しており、オブジェクトの実装ではなく、インターフェイスに基づいてコードを作成する「インターフェイスベースのプログラミング」のアイデアに固執しようと決心しました。 私は十分な数のインターフェースとそれらを実装するクラスを作成し、一歩下がって、クラスが単純であることを認識しました。実装を変更する必要はおそらくないでしょう。クラスは(Phaser.Spriteタンクのように動作するように制限された方法でを移動します)。 次に、YAGNIのアイデアについて数年前に読んだことを覚えています。これは基本的に、コードを過度に設計して、決して使用しない可能性があるものを含めるべきではないということです。 ベストプラクティスに従って、すべてのクラスがインターフェイスを実装する必要がありますか、それとも将来的にスワップアウトされる可能性があると予想されるクラスに限定する必要がありますか?

3
疎結合コードのインターフェースの使用
バックグラウンド 特定の種類のハードウェアデバイスの使用状況に依存するプロジェクトがありますが、必要なことを行うのであれば、誰がそのハードウェアデバイスを作成するかは重要ではありません。そうは言っても、同じことをするはずの2つのデバイスでも、同じ製造元以外のデバイスでは違いがあります。したがって、インターフェイスを使用して、関連するデバイスの特定のメーカー/モデルからアプリケーションを分離し、代わりに、インターフェイスに最高レベルの機能をカバーさせることを考えています。これが私のアーキテクチャが次のようになると私が考えているものです: 1つのC#プロジェクトでインターフェイスを定義しますIDevice。 別のC#プロジェクトで定義されたライブラリに、デバイスを表すために使用される具象があります。 具体的なデバイスにIDeviceインターフェースを実装してもらいます。 IDeviceインタフェースは次のような方法かもしれないGetMeasurementかをSetRange。 アプリケーションに具象に関する知識を持たせ、具象をデバイスを利用する(実装しない)アプリケーションコードに渡しIDeviceます。 アプリケーションに影響を与えずに、使用中のデバイスを変更できるため(これは時々発生するようです)、これが適切な方法であると確信しています。言い換えると、具象の実装方法GetMeasurementやSetRange具象を通して実際に機能する方法は重要ではありません(デバイスのメーカーによって異なる場合があります)。 私の心の中で唯一の疑問は、今やアプリケーションとデバイスの具象クラスの両方がIDeviceインターフェースを含むライブラリに依存しているということです。しかし、それは悪いことですか? また、デバイスとIDevice同じ名前空間に属していない限り、アプリケーションがデバイスについて知る必要がない方法もわかりません。 質問 これは、アプリケーションとそれが使用するデバイスとの間の依存関係を切り離すためのインターフェースを実装するための正しいアプローチのように思えますか?

6
機能要件の欠如は俊敏ですか?
今日、誰もが機敏になりたいと思っています。私が一緒に働いたすべてのチームで、アジャイルの形は異なっていました。いくつかのことは一般的です-毎日のスタンドアップや計画などですが、他の部分は大幅に異なります。 私の現在のチームには、気になることが1つあります。それは機能要件の欠如です。書面による期待が存在しないだけでなく、タスクでは、何を実行する必要があるかが漠然と定義されています。 プロジェクトの目標は、新しいテクノロジーを使用して古いシステムを書き換えることです。古いシステムにも適切なドキュメントはありません。確かに最新のものは存在しません。事業主の要件の説明は、以前と同じように新しい実装で実行しましょう。それは合理的に思えますが、そうではありません。古いシステムは一種のスパゲッティコードであり、そこからビジネス要件を抽出するにはコストがかかります。状況は計画に悪影響を及ぼしているようです。確かに、新しい実装では間違いやバグが発生しやすい(詳細は省略)。 したがって、私は考えています-古いシステムを書き換える場合にビジネス要件がないことは本当に俊敏ですか?

5
戦略パターンと依存性注入を使用して継承を完全に置き換えることはできますか?
例えば: var duckBehaviors = new Duckbehavior(); duckBehaviors.quackBehavior = new Quack(); duckBehaviors.flyBehavior = new FlyWithWings(); Duck mallardDuck = new Duck(DuckTypes.MallardDuck, duckBehaviors) Duckクラスにはすべての動作(抽象)が含まれているため、新しいクラスMallardDuck(拡張Duck)を作成する必要はないようです。 参照:ヘッドファーストデザインパターン、第1章。

5
すべての条件をキャッチするはずのif-elseラダー-冗長なfinal句を追加する必要がありますか?
これは私が最近よくしていることです。 例: setCircle(circle, i, { current }) { if (i == current) { circle.src = 'images/25CE.svg' circle.alt = 'Now picking' } else if (i < current) { circle.src = 'images/25C9.svg' circle.alt = 'Pick failed' } else if (i > current) { circle.src = 'images/25CB.svg' circle.alt = 'Pick chance' } } …

6
依存性注入の最良の定義は何ですか?
誰かが私に連絡して、依存性注入を概念的な方法で定義し、ソフトウェア設計でDIを使用することの実際の長所と短所を説明するように求められるたびに。DIの概念を説明するのが難しいと告白します。単一の責任の原則、継承に関する構成などに関する歴史を伝える必要があるたびに。 開発者のためにDIを説明する最良の方法を説明してくれる人はいますか?

2
JavaおよびC#によってメモリの安全性が提供されるのと同様のプログラミング言語によって、スレッドの安全性をどのように提供できますか?
JavaとC#は、配列の境界とポインターの逆参照をチェックすることにより、メモリの安全性を提供します。 競合状態やデッドロックの可能性を防ぐために、プログラミング言語にどのようなメカニズムを実装できますか?

5
アジャイル方法論:早くて汚いのか、それとも最初に計画するのか?
アジャイルの質問:アジャイルは物事を立ち上げて「迅速かつ汚い方法」で実行することを信じますか、それともアジャイルは根本からしっかりと構築することを好みますか?または、これは方法論の質問ではなく、ケースバイケースで評価する質問ですか? 構造自体の多くをすでに構築した後、私は技術的にシステムの基礎を「再構築」しています...莫大な量の作業ではありません...アジャイルでは、最初にフロー全体を仕様化して分析する必要がありました。微調整してからビルドしますか?この方法の方が良いように感じます...乱雑なシステムを配置すると、それがどのように行われる必要があるかがよくわかります...一方、それはあまり体系化されていません...何が最良の開発であるかに興味があります実践はこれに関してです。 私はプロトタイピングと使い捨てコードについて質問していないので、この質問はアジャイルとプロトタイプとは少し異なると思います。プロダクショングレードのコードのアジャイルに興味があります。
10 agile 

4
どこでもデータチェックを導入するのに適したコードスタイルですか?
プロジェクトのサイズが十分に大きいので、頭の中ですべての側面を維持することはできません。その中でいくつかのクラスと関数を扱っており、データを渡しています。 時間の経過とともにエラーが発生し続けることに気づきました。別の関数にデータを渡すときに、データの正確な形式を忘れてしまったためです(たとえば、ある関数が文字列の配列を受け入れて出力し、別の関数は後で記述します)。辞書などに保持されている文字列を受け入れるため、操作している文字列を配列に入れてから辞書に入れるように変換する必要があります)。 どこがどこでどこが壊れているのかを常に把握する必要がないように、私は各関数とクラスを「分離されたエンティティ」として扱うようになりました。場合によっては、データが間違った形式で指定されている場合は、データを再キャストします。 これにより、渡すデータがすべての関数に「適合」することを確認するために費やす時間が大幅に短縮されました。クラスと関数自体が、入力に問題がある場合に警告を発し(場合によってはそれを修正することもあり)、デバッガーを使用してコード全体を処理し、問題が発生した場所を特定する必要があります。 一方、これによりコード全体も増加しました。 私の質問は、このコードスタイルがこの問題の解決に適切かどうかです。 もちろん、最善の解決策は、プロジェクトを完全にリファクタリングし、データがすべての機能に対して均一な構造を持っていることを確認することです。 。 (参考:私はまだ初心者なので、この質問が素朴だった場合は失礼します。私のプロジェクトはPythonで行われています。)

6
同様に最適化されていない設計を繰り返し繰り返すことをどのように回避しますか?
おそらく多くの人と同じように、設計の問題が頭痛の種になっていることがよくあります。たとえば、問題に直感的に適合し、望ましい利点があるデザインパターン/アプローチがあります。多くの場合、何らかの回避策がないとパターン/アプローチを実装するのが困難になる警告があり、パターン/アプローチの利点が無効になります。多くのパターン/アプローチを繰り返し処理してしまう可能性があります。実際には、簡単な解決策がない実際の状況では、ほぼすべてのパターンに非常に大きな注意事項があるためです。 例: 最近遭遇した実際のものに大まかに基づいた架空の例を紹介します。継承階層が過去のコードのスケーラビリティを妨げていたため、継承ではなくコンポジションを使用したいとしましょう。私はコードをリファクタリングするかもしれませんが、スーパークラス/ベースクラスがサブクラスの機能を単に呼び出す必要があるにもかかわらず、それを回避しようとするいくつかのコンテキストがあることがわかりました。 次善のアプローチは、半分のデリゲート/オブザーバーパターンと半分の構成パターンを実装して、スーパークラスが動作を委任できるようにするか、サブクラスがスーパークラスのイベントを監視できるようにすることです。その場合、クラスを拡張する方法が不明確であるため、クラスの拡張性と保守性が低下します。また、既存のリスナー/デリゲートを拡張するのも難しいです。また、スーパークラスを拡張する方法を確認するために実装を知る必要があるため、情報は十分に隠されていません(コメントを非常に広範囲に使用しない限り)。 したがって、この後は、オブザーバーまたはデリゲートを完全に使用して、アプローチを過度に混同することに伴う欠点を回避することを選択する場合があります。ただし、これには独自の問題があります。たとえば、事実上すべての行動にオブザーバー/デリゲートが必要になるまで、行動の量を増やすためにオブザーバーまたはデリゲートが必要になることに気づく場合があります。1つのオプションは、すべての動作に対して1つの大きなリスナー/デリゲートを持つことですが、実装するクラスは多くの空のメソッドなどで終了します。 それから私は別のアプローチを試すかもしれませんが、それと同じくらい多くの問題があります。それから次のもの、そして次のものなど。 この反復プロセスは、各アプローチが他のアプローチと同じくらい多くの問題を抱えているように見え、一種の設計決定麻痺につながる場合、非常に難しくなります。また、使用する設計パターンやアプローチに関係なく、コードが同じように問題を抱えてしまうことを受け入れるのも困難です。このような状況になった場合、問題自体を再検討する必要があるということですか。この状況に遭遇したとき、他の人は何をしますか? 編集: 私が明確にしたい質問のいくつかの解釈があるようです: OOPは実際にはOOPに固有のものではないことが判明したため、OOPについて質問から完全に除外しました。さらに、OOPについて渡した私のコメントの一部を誤解するのは簡単です。 反復的なアプローチでさまざまなパターンを試す必要があると主張したり、パターンが機能しなくなった場合は破棄したりする必要があると主張する人もいます。これは私が最初に参照するつもりだったプロセスです。これは例からは明らかだと思いましたが、もっと明確にすることができたので、そのために質問を編集しました。

4
コンパイラは型エラーからどの程度正確に回復しますか?
私はいくつかの論文、記事、およびコンパイラー:原則、手法、およびツール(第2版)(別名「ドラゴン・ブック」)のセクション4.1.4、第4章を読み、すべて構文コンパイラーのエラー回復のトピックについて説明しています。ただし、いくつかの最新のコンパイラーで実験した後、構文エラーだけでなく、セマンティックエラーからも回復することがわかりました。 構文的に関連するエラーから回復するコンパイラーの背後にあるアルゴリズムと手法はかなりよく理解していますが、コンパイラーがセマンティックエラーから回復する方法を正確には理解していません。 現在、ビジターパターンのわずかなバリエーションを使用して、抽象構文ツリーからコードを生成しています。コンパイラが次の式をコンパイルすることを検討してください。 1 / (2 * (3 + "4")) コンパイラーは、次の抽象構文ツリーを生成します。 op(/) | ------- / \ int(1) op(*) | ------- / \ int(2) op(+) | ------- / \ int(3) str(4) コード生成フェーズでは、ビジターパターンを使用して、抽象構文ツリーを再帰的に走査し、型チェックを実行します。コンパイラが式の最も内側の部分に到達するまで、抽象構文ツリーをたどります。(3 + "4")。次に、コンパイラーは式の両側をチェックし、それらが意味的に同等ではないことを確認します。コンパイラは型エラーを発生させます。ここに問題があります。今コンパイラは何をすべきですか? コンパイラは、このエラーから回復し、式の外側の部分を型チェックを継続するためには、返却しなければならないいくつかのタイプ(intまたはstrに、表現の最も内側の部分を評価するから)を、次式の最も内側の部分。ただし、単に返すタイプがありません。型エラーが発生したため、型は推定されませんでした。 私が仮定した1つの可能な解決策は、型エラーが発生した場合、エラーが発生し、型エラーが発生したことを示す特別な値が以前の抽象構文ツリートラバーサル呼び出しに返されることです。以前のトラバーサル呼び出しでこの値が検出された場合、抽象構文ツリーのより深いところで型エラーが発生したことがわかっているため、型を推測することは避けてください。この方法は機能するように見えますが、非常に非効率的です。式の最も内側の部分が抽象構文ツリーの奥にある場合、コンパイラーは多くの再帰呼び出しを行って、実際の作業が実行できないことを認識し、それぞれから単に戻る必要があります。 上記の方法を使用していますか(疑わしい)。もしそうなら、それは効率的ではありませんか?そうでない場合、コンパイラがセマンティックエラーから回復するときに使用される方法は正確には何ですか?

5
C ++コードでクラスの相互依存を解決するにはどうすればよいですか?
私のC ++プロジェクトには、2つのクラスとがParticleありContactます。でParticleクラス、Iは、メンバ変数有するstd::vector<Contact> contactsのすべての連絡先含まParticleオブジェクトを、対応するメンバ関数をgetContacts()とaddContact(Contact cont)。したがって、「Particle.h」には「Contact.h」を含めます。 ではContact、クラス、私はのためのコンストラクタにコードを追加したいContactことが呼び出すParticle::addContact(Contact cont)ように、contacts両方のために更新されてParticleいる間でオブジェクトContactオブジェクトが追加されています。したがって、「Contact.cpp」に「Particle.h」を含める必要があります。 私の質問は、これが許容できる/適切なコーディングプラクティスであるかどうかであり、そうでない場合は、達成しようとしていることを実装するためのより良い方法は何ですか(簡単に言えば、新しい連絡先のたびに特定の粒子の連絡先のリストを自動的に更新します)創造された)。 これらのクラスは、NetworkN個の粒子(std::vector<Particle> particles)とNc個の接触(std::vector<Contact> contacts)を持つクラスによって結合されます。しかし、私は次のような関数を使用できるようにしたかったのです。この場合、クラスにparticles[0].getContacts()そのような関数があっても大丈夫ですParticleか、またはこの目的のためにC ++でより良い関連付け「構造」があります(別のクラスで使用されている2つの関連クラスの) 。 私はこれにどのように取り組んでいるのか、ここで視点のシフトが必要かもしれません。2つのクラスはNetworkクラスオブジェクトによって接続されているため、接続情報をNetworkオブジェクトによって完全に制御するのが一般的なコード/クラス編成ですか(パーティクルオブジェクトはその連絡先を認識してはならず、その結果、getContacts()メンバーを持つべきではありません)関数)。次に、特定の粒子が何に接触しているかを知るために、Networkオブジェクトを介して(たとえばを使用してnetwork.getContacts(Particle particle))その情報を取得する必要があります。 Particleオブジェクトがその知識を持つこと(つまり、NetworkオブジェクトまたはParticleオブジェクトのいずれかを介して、その情報にアクセスするための複数の方法があること)は、あまり一般的ではない(おそらく推奨されない)C ++クラス設計でしょうか)?

5
LSPに違反しても大丈夫ですか?
私はこの質問についてフォローアップしていますが、コードから原則に焦点を移しています。 Liskov置換原理(LSP)についての私の理解から、私の基本クラスにあるメソッドは何でも、それらは私のサブクラスに実装する必要があります。このページによれば、基本クラスのメソッドをオーバーライドし、何も実行しないか、例外、あなたは原則に違反しています。 さて、私の問題は次のようにまとめることができます:私は抽象Weapon classと2つのクラス、Swordとを持っていReloadableます。と呼ばれるReloadable特定のが含まれている場合、それにアクセスするにはダウンキャストする必要があります。理想的には、それを回避する必要があります。methodReload()method 次に、を使用することを考えましたStrategy Pattern。このように、各武器は実行可能なアクションのみを認識していたため、たとえば、Reloadable武器は明らかにリロードSwordできますが、リロードすることはできませんReload class/method。Stack Overflowの投稿で述べたように、ダウンキャストする必要はなく、List<Weapon>コレクションを維持できます。 で別のフォーラム、最初の答えができるようにする提案Swordを認識するReloadだけで何もしません、。これと同じ答えが、上でリンクしたスタックオーバーフローのページにもありました。 理由はよくわかりません。なぜ原則に違反し、Swordにを認識さReloadせ、空白のままにするのですか?Stack Overflowの投稿で述べたように、SPで問題がほぼ解決しました。 なぜそれが実行可能な解決策ではないのですか? public final Weapon{ private final String name; private final int damage; private final List<AttackStrategy> validactions; private final List<Actions> standardActions; private Weapon(String name, int damage, List<AttackStrategy> standardActions, List<Actions> attacks) { this.name = name; this.damage = damage; standardActions = new …

1
実生活でクリーンなコードがどのように見えるかを把握する際の問題
私は現在、Robert C. Martinによる「クリーンコード:アジャイルソフトウェアのクラフトマンシップのハンドブック」を読んで作業しています。著者は、関数が1つのことだけを行うべきであり、したがって比較的短い方法について話します。具体的にはマーティンはこう書いている: これは、ifステートメント、elseステートメント、whileステートメントなど内のブロックが1行の長さであることを意味します。おそらくその行は関数呼び出しでなければなりません。これにより、囲まれた関数が小さく保たれるだけでなく、ブロック内で呼び出された関数にわかりやすい名前を付けることができるため、記録的な価値が追加されます。 これは、関数が入れ子構造を保持するのに十分な大きさであってはならないことも意味します。したがって、関数のインデントレベルは1または2以下にする必要があります。もちろん、これにより機能が読みやすくなり、理解しやすくなります これは理にかなっていますが、私がクリーンなコードと見なしているものの例と矛盾しているようです。たとえば、次の方法を考えます。 public static boolean millerRabinPrimeTest(final int n) { final int nMinus1 = n - 1; final int s = Integer.numberOfTrailingZeros(nMinus1); final int r = nMinus1 >> s; //r must be odd, it is not checked here int t = 1; if (n >= 2047) { …
10 clean-code 

7
なぜ内部アプリケーション用の内部ライブラリを開発するのですか?
内部アプリケーションの開発専用に使用する内部ライブラリを開発する必要がある理由を理解できません。組織外の誰かが作成したソフトウェアを使用したい場合は、ヘッダーファイルと.aまたは.soファイルを送ってプロジェクトにリンクするだけでよい(同じ環境でコンパイルされていると仮定) 。 しかし、ヘッダーと実装ファイルにアクセスでき、それらをソースツリーに含めてすべて一緒にコンパイルできるときに、内部ライブラリを内部アプリケーションにリンクするためだけに開発する必要があるのはなぜですか? つまり、ソースコードが記述されている場合、それをバイナリライブラリにコンパイルしてアプリケーションにリンクするか、プロジェクトのソースファイルに含めて定期的にコンパイルするだけにするかをどのように決定しますか? 各プロジェクトに「インクルード」ファイルと言っても、現在開発中のプロジェクトのソースツリーに各ファイルをコピーして貼り付けることを意味するものではありません。通常の方法でプロジェクトのファイルに含めることができる共通のソースコード(#includeなど)を含む(任意のプロジェクトとは別の)ディレクトリ/ライブラリを開発することを意味します。 psここでは、複数のデスクトップアプリケーション用のc / c ++開発について話しています。
10 libraries 

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.