 Welcome to module number six, in which we will discuss software design and implementation. And design is often as a term used to describe also what we discussed in the previous module, namely the architecture of a system. So as a start, the question arises, what is the difference really between software design and software architecture? And at least in this course, we use the distinction that architecture is more the high level structure. It's the overall system quality trade-offs, for example, performance versus security. Whereas design is more on the lower level. How do we decide what classes we have and what they should be doing, functions and relationships between them? So in a way, architecture is the macro structure, design is the micro structure of a system. And we'll discuss a number of things, but in general design and implementation as sort of the least abstract part of software engineering is probably what you are most familiar with already. If you know programming, there are already some things that you might be doing or you're familiar with and we'll explain some of them. But in general, I focus on a number of things in this module. And that is the concept of reuse of software, parts of the software. It's the configuration management. And finally, it is the so-called host target development. And these are three things that are very important. Reuse, of course, you can reuse software parts at a very different level and different artifacts. So you can, of course, if you have an entire system that suits your requirements, you could be reusing the entire system. You can reuse individual components. So maybe you have a backend that fits your application, you reuse it somewhere else and it might do the trick as well. You have, of course, all of you have already reused libraries, for example, a testing tool or a library for accessing a database. So that's a very common way of reusing functionality at a very low level of the design. And then finally, what you have seen last time in the architecture lecture is to reuse abstractions. So we have seen the architectural styles, which are a way of reusing knowledge about how to design a system, how to architect the system, and reuse that in a way in an architectural style. We have a similar thing in the design. We have so-called design patterns, which we'll go into that basically focus on smaller parts of the system. How do you design a number of classes so that they fulfill a certain purpose in a good practice kind of way? But if we talk about reuse, we have to discuss that reuse sounds nice, but there is always a cost associated with reusing software parts. And that is due to different reasons. But for example, if you take a library, it might not be fitting you exactly. So you might have to adapt certain things, adaptation cost, which is also very common, for example, for components, for larger system parts. They might not fit everything. You might have license costs. So a library or a component might be costing money because there's a supplier that develops that and would like to make a living of it. So you might have to purchase it or to license it in a certain kind of way. You always, even if you don't have to have adaptations, you somehow need to integrate it into your system to get it to run. That can take time. And finally, the time for actually searching for it, for browsing the internet or maybe in terms of larger systems and components for having a process of scoring different suppliers takes time. And that is, of course, costly in itself. So reuse is a good aim, is a goal, and it can help you tremendously, both in the amount of the quality you get, but also the time you save. But only if the cost to actually reuse is justified. And this is, of course, a business decision where companies regularly have to decide, do we really take something from someone else or do we rather implement it ourselves? So that's the reuse part. Then another part which is really important in software design and implementation is configuration management. And that is really a number of issues. It's, first of all, having access to the right versions of your software. So many of you have so far only ever implemented one product and slowly evolved it. But in many cases, in the real life, you have different versions of your software that are in use. And this doesn't matter whether you're developing a small library or it's some embedded system like an airplane where things are really built physically in. But you might have to keep track of what are the different versions that we are being using. For example, in this airplane, we're using version one in this airplane's version two or in our library's version one. And you need to have support for these. And then having the right tools to actually access the right version at the right time is tremendously important. And here we are talking about tools such as Git, which you are probably familiar with. So version control tools that are in use today. Then we are also talking about things like compilation support or compilation and linking support. So having access to the right versions of the libraries you need, the right compiler, very important and embedded systems. There are often very small differences between the compilers, but you need a specific compiler for this hardware or for that hardware. And having a tool that allows you to quickly access those things is really important. And in practice what this means is that there are lots and lots of tools and scripts and smaller programs in place that make sure that you can at any time access these things in a good way. Finally, what we also often consider under configuration management is the ability to track changes. So you, for example, discover a bug in your system. Do you have some kind of way of reporting this? So it is actually connected to the right version, to the right compiler and so on. So many of the tools like Jira that are in practice allow you to be able to actually link that to Git. So you have a right commit. But it's also about smaller things. So issue tracking and change management is one way of addressing this, but it's also about, for example, using commit messages. So actually having a version control system and people commit something and they describe it so that when you go through the version history, you try to find out why there is a bug. You can understand what has been changed. So it's both having the right tools, having the possibilities, but also having the right practices in place, having the developers actually writing proper commit messages. And if you look at, for example, project work you're doing, if you look into the commit log, very often you just see a bunch of meaningless commit messages because people think it's just a waste of time to quickly write something. But for configuration management for tracking what's going on, this is highly important. Okay. And as a last aspect for now, there is the issue of host target development, which again many of you might not yet be familiar with. Host target development essentially describes that you are developing on a certain environment, on a certain host, on your computer, but you are developing for a specific target. So for instance, you might be developing on a Windows computer with a certain hardware like an Intel CPU, but you are developing for an Android phone with different operating system, different hardware. So the question is how do you do this in a good way? And of course, mobile apps are a good example, but it's very much the same if you do a web application, for example. You have different browsers, different versions, different operating systems. Embedded systems are a very extreme case of that where there's really small differences depending on the CPU or the kind of hardware boards you use. And all of this is under the term of host target development. So we are kind of concerned with things like what is the hardware, what is the operating system? What are maybe the databases or other dependencies that we might have? Do we get them to run? As already mentioned here, compilation support in host target development. Similarly, it's very important that we have the right compilers, we have the right linkers, we have debuggers. We might have things like simulators that can actually simulate the real thing, even if we don't have it. For example, if you don't have an Android phone, you might still want to use the Android emulator or simulator to see how this would look like. And then if you go more advanced, we have so-called in-the-loop testing. So if you develop a larger system like an airplane, you cannot always just put the software on the real airplane and test it because you might not be able to actually fly that. The airplane might not yet exist. You're just developing a part of the system that is ongoing in development. So you have so-called in-the-loop testing, which means you often start with just running it on your computer, on your hardware, and then slowly you add pieces to it. If you have a so-called hardware in-the-loop test, it means you're testing your software together with the real hardware. There is something that is called a model in-the-loop. That means that you are running your software, and there is some kind of simulator, a model that runs behind it that tries to, for example, behave like the real aerodynamics. It gives you the real sensor values depending on the simulator. So these kind of technologies come into play there. All of this is our concerns that we look at when we develop software, when we want to do the right design and implementation. And of course the target in the end is we want to have an executable system that is of high quality, that is maintainable, that is reusable maybe. And this is what we'll dive into more now in the remainder of this module. So we'll look at best practices, good practices in implementation. We will look at design patterns in particular.