Skip to content

Commit

Permalink
Merge pull request #98 from donnachaforde/drafts
Browse files Browse the repository at this point in the history
Publish 2 x posts on Design
  • Loading branch information
donnachaforde authored Feb 20, 2024
2 parents 98d67b4 + c871ee6 commit c94e6e8
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 2 deletions.
14 changes: 12 additions & 2 deletions blogs+posts/software-engineering/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,22 @@ Here are posts and articles covering more general software engineering topics.
## Management & Leadership

* [Repetition, Repetition, Repetition](./repetition) - Lessons from Sports Management that cross over to software engineering.

* [I think you'll find it's more complicated than that](./more-complicated) - Knowledge Transfer opportunities in Agile ceremonies.

* [Becoming a Coach](./coaching) - Considerations on becoming a volunteer Sports Coach.

* [Think on Paper](./think-on-paper) - Clarifying your thought process and helping your decision making.


## API Design
## Design

* [Be considerate in your API design, but not overly so](./api-design) - An example of when being overly helpful can be to the detriment of your API design.

* [Antipattern - STL Inheritance](./antipattern-stl-inheritance/) - An explanation as to why you probably don't want to inherit from that STL container

* [Template Extension in C++](./template-extension/) - A note on how templates are extended in the STL to 'borrow' behaviour compared to how OO inheritance achieves it.

* [Be considerate, but not overly so](./api-design) - An example of when being overly helpful can be to the detriment of your design.

## Performance Engineering

Expand All @@ -23,7 +31,9 @@ Here are posts and articles covering more general software engineering topics.
## Designing the 'espresso' Library

* [Design Considerations & Influences](./espresso/design-considerations) - Thoughts and influences on the design of the espresso library.

* [Object Creation Patterns](./espresso/object-construction) - Discussion on constructors and factory classes, as applied to the espresso library.

* [Utilizing Manager Objects](./espresso/manager-objects) - Discussion on the advantages of deploying a manager object and the separation of function.

## Developing C++ with Visual Studio
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
[home/](../../../)[blogs+posts/](../../)[software-engineering/](../)[antipattern-stl-inheritance](./)


# Design Antipattern - STL Inheritance


<img src="https://upload.wikimedia.org/wikipedia/commons/1/18/ISO_C%2B%2B_Logo.svg" width="300" height="300" >

_Photo Credit: [wikimedia](https://upload.wikimedia.org/wikipedia/commons/1/18/ISO_C%2B%2B_Logo.svg)_

### Never, Ever Inherit from an STL Container

That is unless you're following the style and convention of the STL library to create a template class to represent a modified form of container. Otherwise, you're most probably applying an antipattern to your design and you'll likely end up with unexpected behaviour, resulting in memory leaks.

Why?

Well, because it's wrong on both of two very important points: 1) **Design** and 2) **Implementation**.

#### Design

If you find yourself tempted to inherit from an STL container because your class represents a list or some other grouping, then you might be violating the design difference between 'is a' and 'has a'. Just because the class you're building represents a collection of things doesn't mean it 'is a' collection of things. It more likely means that it 'has a' collection of things. _(See Item 32 and Item 38 in Effective C++ Third Edition by Scott Meyers)_

For example, if you consider your class as representing a list of things, then you likely needs to contain a `vector<thing>` things rather than inheriting from it.

The following code represents a 'is a' design.
````C++
class Foo : public vector<thing>
{
// asking for trouble
.
.
.
};
````
Whereas the following represents a 'has a' design.
````C++
class Foo
{
private:
// 'has a'
vector<thing> myListofThings;
.
.
.
};
````


#### Implementation

The STL was not designed to be extended through normal OO class inheritance. Apart from the fact that the STL represents _template classes_ rather than actual concrete classes, the most important factor is that the STL was designed with performance in mind from the outset and intentionally excluded _virtual functions_ in its implementation. That includes _virtual destructors_, which means that if you inherit from a container, you're not going to achieve the correct order of object destruction you might be depending on to release heap memory and other resources, resulting in memory leaks and dangling resources.


#### Summary
One of the worst examples of a memory leak I ever had to deal with in my career was because of this _antipattern_. I was given the task of converting a batch system into a real-time service and once the system was developed, a catastrophic memory leak in the underlying libraries became apparent. It'd never been noticed in the batch system because those libraries were exercised only once before the process terminated whereas now, they were embedded in an online, always-on service, which meant they'd be exercised over-and-over without the process terminating (and memory being released).

Actually, getting to that point took effort in itself because the server crashed immediately upon innovation, which meant the issue couldn't be debugged in a debugger. It took some ol' fashioned crash analysis by-hand to determine the cause of the crash might actually be a lack of available memory. And, in turn, that that had been caused by earlier leaks. Further still, it wasn't just a handful of leaks but rather a collection of thousands of minor leaks that collectively added up to a massive leak (relative to the typical machine specs of the time).


Unfortunately, the _antipattern_ had been applied liberally, which meant there were several classes affected and in turn, associated references (e.g. _iterators_, etc.) were rampant throughout the codebase. That meant that, having diagnosed the problem, the majority of the work had only began because so much of the codebase was affected.

In order to cope with the extent of the disruption and minimize impact, I modelled constructs like `iterator` and `const_iterator` as well as methods like `begin()` and `end()` within the classes themselves. So began iteration after iteration of breaking the class inheritance in order to have the compiler let me know of all the broken references. After several weeks of this painstaking work, I eventually managed to get the issue under control and the server could actually function.

The point is that all of this rework could have been avoided had the original author simply known that the STL containers aren't designed to be extended through inheritance.


***
_Donnacha Forde_

_[linkedIn.com/in/donnachaforde](https://www.linkedin.com/in/donnachaforde)_




67 changes: 67 additions & 0 deletions blogs+posts/software-engineering/template-extension/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
[home/](../../../)[blogs+posts/](../../)[software-engineering/](../)[template-extension](./)



## A Note on Template Extension

In C++, _class_ and _template class_ are different things, which show up in the way that each are used to model the problem domain. I like to use language like _"you inherit from a class"_ but _"you extend a template"_. Sure, both are physically extending the thing but the effect is that you expand the function of a class whereas you restrict the function of a template (at least in the STL).

In typical OO design, you start with an _interface_ and perhaps an _abstract class_ so that you may generalize and rely on polymorphism. For example, if I were modelling shapes, I might have something like the following:

````C++
interface IShape
{
void draw();
void print();
};

interface ICircle : public IShape
{
double getRadius();
};


class Circle : public ICircle
{
...
};

````

In broad terms, you extend the functionality of the parent. You typically extend the interface by adding to it, thus introducing new behaviour.

However, the design of the STL could almost be described as the inverse of this. The topmost templates contain the fullest functionality and the 'inherited' templates effectively restrict functionality. Technically, inheritance isn't actually used but rather 'implemented in terms of' (often modelled in C++ as _private inheritance_).

A good example of this is the implementation of the `queue<>` template, which is implemented in terms of a `deque<>` - i.e. a 'double-ended queue'. This makes sense in so much as a `queue<>` is a restricted form of a `deque<>`. Whereas it's possible to traverse a `deque<>` in either direction, a `queue<>` can only be traversed in one direction, hence it's a 'queue'.

Here's the STL code declaring `queue<>`:

````C++
template <class T, class _Container = deque<T>>
class queue
{
.
.
.

protected:
_Container c{};
};
````
Note the second template argument, which defaults to `deque<T>`, is used to declare the only member variable in `class Queue<T>` and is what's used to provide the implementation of `queue<T>`. **The queue is implemented in terms of deque.**



If you set out to model this sort of construct in a Object-Oriented class library (which was popular in the '90s with libraries like RougeWave), you'd probably design it along the lines of first defining `class queue` containing elements with a single pointer to the next element and then extend it through inheritance to define `class deque` with an additional pointer to the previous element. But, in generic or template design, you invert that approach and define `template <T> class deque` first and then define `template <T> class queue` as a restricted form of `deque`.

This illustrates an important way Object-oriented design differs from 'Generic' design. Remember, C++ can support multiple languages and paradigms and that what we have today in C++ development is arguably a _hotpotch_ of technology and paradigms with a mix of OO and Generics.


***
_Donnacha Forde_

_[linkedIn.com/in/donnachaforde](https://www.linkedin.com/in/donnachaforde)_




0 comments on commit c94e6e8

Please sign in to comment.