質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

3回答

2378閲覧

似た処理を行う関数が複数あるプログラムの設計

deeeeeeact

総合スコア1

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/06/04 12:05

前提・実現したいこと

プログラムの設計に関する質問です。

XMLファイルがあり、コマンドライン引数にアクション(--add, --mod, --del) + パス(XPATH)を入力してもらい、
それぞれパス(値)の追加。値の変更。パス(値)の削除を行うpythonプログラムを作成しております。

add, mod, removeの処理を行う関数を作成し、それぞれ該当する処理を行っているのですが
各関数では似たような処理(ファイルを開く、パスの存在確認、ファイル書き込み)を行っているため、
プログラム的に美しくないです。どのように設計すれば良いものでしょうか?

read、write関数を作成して共通化するのが一般的なのでしょうか?
オブジェクト指向をあまり理解できていないのですが、XMLFileクラスなどを作成したりするのが良いのでしょうか?
プログラム作成の諸先輩方のアドバイスをいただきたいです。
恐れ入りますがご教示のほど、よろしくお願いいたします。

※ソースコードはざっくりとしたイメージでスミマセン。

発生している問題・エラーメッセージ

エラーメッセージ

該当のソースコード

def add(path): # 1. ファイルを開く処理 # 2. パスの存在確認処理 # 3. 追加処理 # 4. ファイルへ書き込み処理 pass def mod(path): # 1. ファイルを開く処理 # 2. パスの存在確認処理 # 3. 変更処理 # 4. ファイルへ書き込み処理 pass def del(path): # 1. ファイルを開く処理 # 2. パスの存在確認処理 # 3. 削除処理 # 4. ファイルへ書き込み処理 def check_args(): parser = argparse.ArgumentParser() group = parser.add_mutually_exclusive_group() group.add_argument('--add', action='store_true') group.add_argument('--mod', action='store_true') group.add_argument('--del', action='store_true') parser.add_argument('--path', required=True) args = parser.parse_args() if not any([args.add, args.mod, args.remove]): parser.error('arg error') return args def main(): args = check_args() if args.add: ret = add(args.path) elif args.mod: ret = mod(args.path) elif args.del: ret = del(args.path) else: print('invalid') ret = 1 return ret if __name__ == '__main__': sys.exit(main())

試したこと

補足情報(FW/ツールのバージョンなど)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takasima20

2021/06/04 12:46

美しさってのも個人の感覚になるし、このままでもいいんじゃ? どうしてもってんなら読み書きを main に移すくらい?
guest

回答3

0

ベストアンサー

高階関数の利用

Python

1def make_file_operation(f): 2 def inner(path): 3 print(f'{path}を開きます') 4 f(path) 5 print(f'{path}を閉じます') 6 7 return inner 8 9add = make_file_operation(lambda p: print(f'{p}に要素を追加')) 10mod = make_file_operation(lambda p: print(f'{p}の要素を修正')) 11del_ = make_file_operation(lambda p: print(f'{p}の要素を削除'))

追記:
コメントでactorbugさんがご指摘のように、デコレータを使った方が読み良いです。

Python

1@make_file_operation 2def add(p): 3 print(f'{p}に要素を追加')

コンテキストマネージャを定義

Python

1class XmlContextManager: 2 def __init__(self, path): 3 self._path = path 4 5 def __enter__(self): 6 print(f'{self._path}を開きます') 7 8 def __exit__(self, exc_type, exc_value, traceback): 9 print(f'{self._path}を閉じます') 10 11def add(path): 12 with XmlContextManager(path): 13 print(f'{path}に要素を追加') 14 15def mod(path): 16 with XmlContextManager(path): 17 print(f'{path}の要素を修正') 18 19def del_(path): 20 with XmlContextManager(path): 21 print(f'{path}の要素を削除')

追記:
contextlibなんてものもあります。簡潔に書けて便利。

Python

1import contextlib 2 3@contextlib.contextmanager 4def XmlContextManager(path): 5 print(f'{path}を開きます') 6 yield 7 print(f'{path}を閉じます')

ただし必要以上に抽象化するとメンテナンスがめんどくさいのでご注意を。

投稿2021/06/04 13:07

編集2021/06/04 14:04
LouiS0616

総合スコア35668

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

actorbug

2021/06/04 13:42

高階関数の方は、デコレータとして使った方が書きやすいかもしれません。 @make_file_operation def add(p): print(f'{p}に要素を追加')
LouiS0616

2021/06/04 13:57

@actorbug さん 確かにその方が良いですね。コメントありがとうございます。
LouiS0616

2021/06/04 13:58

そう言えばコンテキストマネージャもジェネレータ関数風味に書けたような。
deeeeeeact

2021/06/05 07:17

デコレータを用いた書き方が、一番納得がいきました。 抽象化しすぎるとメンテンナンスが面倒だという事も考慮しながら、今後に活かしていきたいと思います。 回答ありがとうございました。
guest

0

設計は、基本設計(目的)、機能設計(内容、ユーザインタフェース)、構成設計(部品、部品間インタフェース)、詳細設計(処理論理)などがあります。

deeeeeeactさんの質問は構成設計2関する質問であるように思われます。

構成設計は、共通部分をまとめ、部品間のインタフェースを最小限にすることです。
共通部分をまとめるのは、変更のコストを最小限にするためです。
部品間のインタフェースを最小限にするのはバグを防ぎ、保守を容易にするためです。

今回の場合は以下の表を見て、目的と処理の関係をよく考えてみましょう。

+-----------------------------+--------+--------+--------+
|              | add | mod | del |
+-----------------------------+--------+--------+--------+
|# 1. ファイルを開く処理   | | | |
+-----------------------------+--------+--------+--------+
|# 2. パスの存在確認処理   | | | |
+-----------------------------+--------+--------+--------+
|# 3. 追加処理        | | | |
+-----------------------------+--------+--------+--------+
|# 4. ファイルへ書き込み処理 | | | |
+-----------------------------+--------+--------+--------+

縦は処理の違いであり、横は目的の違いです。
目的が違っても処理が全く同じならひとつ作ってそれを使う方が良いでしょう。
その制御を大きなレベルでやるか、細かいレベルでやるかは、設計方針の話です。
#1、#2、#3、#4でインタフェースを簡単にするにはどいういうデータを受け渡しするのかをよく考えましょう。

大事なのは自分自身でよく考えることで、オブジェクト指向とかデザインパターンを丸覚えすることではありません。
自分自身で悩んで作ってみて、失敗して反省するというプロセスを経験して初めて、オブジェクト指向とかデザインパターンの本当の意味がわかってくるものです。

投稿2021/06/04 12:50

ppaul

総合スコア24670

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

deeeeeeact

2021/06/05 07:20

回答ありがとうございます。 非常にわかりやすい説明ありがとうございます。 設計の難しさを感じていますが、試行錯誤しながら学んでいきたいと思います。
guest

0

デコレータを使用した案を採用しました。
回答いただいた方、ありがとうございました。

投稿2021/06/05 07:21

deeeeeeact

総合スコア1

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問