- Modular Programming with Python
- Erik Westra
- 2503字
- 2021-07-16 10:47:18
Designing the inventory control system
If you step back and review our inventory control program's functionality, you can see that there are three fundamental types of activity that this program needs to support:
- Storing information
- Interacting with the user
- Generating reports
While this is very general, this breakdown is helpful because it suggests a possible way of organizing our program code. For example, the part of the system responsible for storing information could store the lists of products, locations, and inventory items and make this information available as required. Similarly, the part of the system responsible for interacting with the user could prompt the user to choose an action to perform, ask them to select a product code, and so on. Finally, the area of the system responsible for generating reports would be able to generate each of the desired types of report.
Thinking about the system in this way, it becomes clear that each of these three parts of the system could be implemented as a separate module:
- The part of the system responsible for storing information could be called the data storage module
- The part of the system responsible for interacting with the user could be called the user interface module
- The part of the system responsible for generating reports could be called the report generator module
As the names suggest, each of these modules perform a particular purpose. In addition to these special-purpose modules, we are going to need one more part to our system: a Python source file that the user executes to start up and run the inventory control system. Because this is the part the user actually runs, we will call this the main program, which is often stored in a Python source file named main.py
.
We now have four parts to our system: three modules plus a main program. Each of these parts will have a particular job to do, and the various parts will often interact to perform a particular function. For example, the report generator module will need to obtain a list of the available product codes from the data storage module. These various interactions are represented by arrows in the following diagram:

Now that we have an idea of the overall structure for our program, let's take a closer look at each of these four parts to see how they will work.
The data storage module
This module will be responsible for storing all of our program's data. We already know that we'll need to store three types of information: a list of products, a list of locations, and a list of inventory items.
To keep our program as simple as possible, we will make two major design decisions regarding the data storage module:
- The lists of products and locations will be hardwired into our program
- We will hold the list of inventory items in memory and save it to disk whenever the list changes
A more sophisticated implementation of our inventory control system would store this information in a database and allow the user to view and edit the lists of product codes and locations. In our case, however, we are more interested in the overall structure of our program, so we want to keep the implementation as simple as possible.
While the list of product codes will be hardwired, we don't necessarily want to build this list into the data storage module itself. The data storage module is responsible for storing and retrieving information—it isn't the data storage module's job to define the list of product codes. Because of this, we are going to need a function within the data storage module that can be called to set the list of product codes. This function will look like the following:
def set_products(products): ...
We've already decided that for each product, we want to store the product code, a description, and the desired number of items that the user wants to keep in their inventory for that type of product. To support this, we're going to define the list of products (as supplied in the products
parameter to our set_products()
function) as a list of (code, description, desired_number)
tuples. For example, our list of products might look something like this:
[("CODE01", "Product 1", 10), ("CODE02", "Product 2", 200), ... ]
Once the list of products have been defined, we can provide a function to return this list as needed:
def products(): ...
This would simply return the list of products, allowing your code to work with this list as needed. For example, you can scan through the list of products using the following Python code:
for code,description,desired_number in products(): ...
These two functions allow us to define the (hardwired) list of products and retrieve this list whenever we need it. Let's now define the equivalent two functions for the list of locations.
First, we need a function to set the hardwired list of locations:
def set_locations(locations): ...
Each item in the locations
list will be a (code, description)
tuple, where code
is the code for a location and description
is a string describing the location so that the user knows where it is.
We then need a function to retrieve this list of locations as needed:
def locations(): ...
Once again, this returns the list of locations, allowing us to work with these locations as required.
We now need to decide on how the data storage module will allow the user to store and retrieve the list of inventory items. An inventory item is defined as a product code plus a location code. In other words, an inventory item is a particular type of product at a particular location.
To retrieve the list of inventory items, we'll use the following function:
def items(): ...
Following the design we used for the products()
and locations()
functions, the items()
function will return a list of the inventory items, where each inventory item is a (product_code, location_code)
tuple.
Unlike the lists of products and locations, however, the list of inventory items will not be hardwired: the user will be able to add and remove inventory items. To support this, we're going to need two more functions:
def add_item(product_code, location_code): ... def remove_item(product_code, location_code): ...
There is only one more part of our data storage module that we need to design: since we know that we'll be storing the list of inventory items in memory and saving them to disk as required, we're going to need some way of loading the inventory items from disk into memory when the program starts. To support this, we're going to define an initialization function for our module:
def init(): ...
We've now decided on a total of eight functions for the data storage module. These eight functions make up the public interface for our module. In other words, the other parts of the system will only interact with our module using these eight functions:

Notice the process we went through here: we started by looking at what our module needed to do (in this case, storing and retrieving information) and then designed the module's public interface based on those requirements. For the first seven functions, we used our business requirements to help us design the interface, while for the final function, init()
, we used our knowledge of how the module will work internally to change the interface so that the module can do its job. This is a common way of working: both the business requirements and the technical requirements will help shape the module's interface and how it interacts with the rest of the system.
Now that we've designed our data storage module, let's repeat the process for the other modules in our system.
The user interface module
The user interface module will be responsible for interacting with the user. This includes asking the user for information, as well as displaying information on the screen. To keep things simple, we will use a simple text-based interface for our inventory control system, using print()
statements to display information and input()
to ask the user to enter something.
A more sophisticated implementation of our inventory control system would use a graphical user interface with windows, menus, and dialog boxes. Doing this would make the inventory control system much more complicated and is well beyond the scope of what we are trying to achieve here. However, because of the modular design of the system, if we were to rewrite the user interface to use menus, windows, and the like, we would only be changing this one module—the rest of the system would be unaffected.
Note
This is actually a slight oversimplification. Replacing a text-based interface with a GUI requires many changes to the system, and would probably require us to change the module's public functions slightly, just like we had to add an init()
function to the data storage module to allow for the way it worked internally. However, because of the modular way we're designing our system, the other modules would not be affected if we rewrote the user interface module to use a GUI.
Let's think about the various tasks our inventory control system needs to perform from the point of view of the user's interaction with the system:
- The user needs to be able to select an action to perform.
- When the user wants to add a new inventory item, we need to prompt the user for the details of the new item.
- When the user wants to remove an inventory item, we need to prompt the user for the details of the inventory item to remove.
- When the user wishes to generate a report, we need to be able to display the contents of the report to the user.
Let's work through these interactions one at a time:
- To select an action to perform, we'll have a
prompt_for_action()
function which returns a string identifying the action that the user wishes to perform. Let's define the codes that this function can return for the various actions the user can perform: - To add an inventory item, the user will need to be prompted for the details of the new item. Because an inventory item is defined as a given product at a given location, we actually need to prompt the user to choose both the product and the location for the new item. To prompt the user to select a product, we will use the following function:
def prompt_for_product(): ...
The user will be shown a list of the available products and then choose an item from the list. If they cancel,
prompt_for_product()
will returnNone
. Otherwise, it will return the product code for the selected product.Similarly, to prompt the user to select a location, we will define the following function:
def prompt_for_location(): ...
Once again, this displays a list of the available locations, and the user can choose a location from the list. If they cancel, we return
None
. Otherwise, we return the location code for the selected location.Using these two functions, we can ask the user to identify a new inventory item, and then we use the data storage module's
add_item()
function to add it to the list. - Because we are implementing this as a simple text-based system, the process of removing an inventory item is almost identical to the process used to add an item: the user will be prompted for the product and location, and the inventory item at that location will be removed. Because of this, we won't need any additional functions to implement this feature.
- To generate a report, we will simply call the report generator module to do the work, and then we display the resulting report to the user. To keep things simple, our reports won't take any parameters, and the resulting report will be displayed in plain-text format. Because of this, the only user interface function that we will need is a function to display the plain-text contents of the report:
def show_report(report): ...
The
report
parameter will simply be a list of strings containing the generated report. All theshow_report()
function needs to do is print out these strings, one at a time, to show the contents of the report to the user.
This completes our design for the user interface module. There are a total of four public functions which we will need to implement for this module.
The report generator module
The report generator module is responsible for generating reports. Since there are two types of report that we need to be able to generate, we are simply going to have two public functions in the report generator module, one for each type of report:
def generate_inventory_report(): ... def generate_reorder_report(): ...
Each of these functions will generate a report of the given type, returning the report's contents as a list of strings. Note that there are no parameters to these functions; because we are keeping things as simple as possible, the reports won't use any parameters to control how they are to be generated.
The main program
The main program isn't a module. Instead, it is a standard Python source file that the user runs to start the system. The main program will import the various modules it needs, and call the functions we have defined to do all the work. In a sense, our main program is the glue that binds together all the other parts of the system.
In Python, when a source file is intended to be run (as opposed to being imported and used by other modules or from the Python command line), it is common to use the following structure for the source file:
def main(): ... if __name__ == "__main__": main()
All of the program's logic is written inside the main()
function, which is then called by the final two lines in the file. The if __name__ == "__main__"
line is a piece of Python magic that basically means if this program is being run. In other words, if the user is running this program, call the main()
function to do all the work.
Note
We could put all the program's logic beneath the if __name__ == "__main__"
statement, but there are some advantages to putting our program's logic in a separate function. By using a separate function, we can simply return from this function when we want to exit. It also makes error handling easier, and the code is better organized because our main program code is separate from the code that checks whether we are actually running the program.
We are going to use this design for our main program, putting all the actual functionality within a single function called main()
.
Our main()
function is going to do the following:
- Call the
init()
function for the various modules which need to be initialized. - Provide the hardwired lists of products and locations.
- Ask the user interface module to prompt the user for a command.
- Respond to the command entered by the user.
Steps 3 and 4 will be repeated indefinitely until the user quits.
- 微信公眾平臺與小程序開發:從零搭建整套系統
- Python數據分析基礎
- Python數據可視化之Matplotlib與Pyecharts實戰
- 編程數學
- Mastering Linux Security and Hardening
- Learning Apache Cassandra
- Python項目實戰從入門到精通
- Java Fundamentals
- Developing SSRS Reports for Dynamics AX
- Node學習指南(第2版)
- Raspberry Pi Robotic Projects(Third Edition)
- C#面向對象程序設計(第2版)
- WCF技術剖析(卷1)
- 快樂編程:青少年思維訓練
- Python第三方庫開發應用實戰