-
Notifications
You must be signed in to change notification settings - Fork 0
Method Stereotypes
A method stereotype is a concise high-level description of the role of a method in a software system. It is a simple form of documentation that is used to provide additional information about the behavioral aspects and responsibilities of a method. Method stereotype information can be used to lay the foundation of more sophisticated forms of documentations and design recovery.[1][2]
Some uses of method stereotype information include determining the stereotypes of classes,[5] improving the precision of metrics calculations,[1] measuring the level of improvement or degradation of a method when its role changes,[3] improving feature location in source code,[4] generating natural language summaries for methods,[6] characterizing commits on version control systems into different types, [7] and creating signature descriptors for software systems.[8]
Method stereotypes can be classified into five main categories depending on the role of a method:
Stereotype Category | Stereotype | Description |
---|---|---|
Structural Accessor | get | Returns a data member |
non-const-get (C++) | ||
predicate | Returns Boolean value that is not a data member | |
non-const-predicate (C++) | ||
property | Returns non-boolean value that is not a data member | |
non-const-property (C++) | ||
accessor | Returns information about data members through parameters | |
non-const-accessor (C++) | ||
Structural Mutator | set | Modifies a data member |
command | Performs a complex change to the object’s state. | |
non-void-command | ||
Creational | constructor | Creates and/or destroys objects |
copy-constructor | ||
destructor | ||
factory | ||
Collaborational | collaborator | Works with objects belonging to classes other than itself. Changes the state of the current object |
controller | Works with objects belonging to classes other than itself. Changes only an external object’s state (not this) | |
Degenerate | stateless (a.k.a incidental or pure stateless) | Does not read or change an object’s state directly nor indirectly. Has no calls of any type |
wrapper (a.k.a stateless) | Does not read or change an object’s state directly. Has one call of any type | |
empty | Has no statements |
TABLE 1. Taxonomy of Method Stereotypes.
A structural accessor is a method that returns information about the state of an object. There are four types: get, predicate, property, and void-accessor.
A get is a method returns the value of some data member (field or attribute).
A non-const-get method (C++ only) is a get method that is not constant.
Examples for C++:
// @stereotype get
double AxisModelBase::getTickStep() const {
return m_tick_step;
}
// @stereotype get
bool RootBranch::isUseable() const {
return m_useable;
}
// @stereotype non-const-get
std::list<std::string>& XmlController::getMissingTuples( ) {
return m_missing_tuples;
}
Examples for C#:
// @stereotype get
protected override string GetSourceObjectEventName()
{
return _eventName;
}
internal string Namespace
{
// @stereotype get
get
{
return this.nameSpace;
}
}
// @stereotype get
internal int Buffercapacity()
{
return _capacity;
}
Examples for Java:
// @stereotype get
public final boolean isTimeout() {
return timeout;
}
// @stereotype get
public static boolean isLockdown() {
return lockdown;
}
// @stereotype get
long cachedEventCount() {
return size;
}
A predicate is a method that computes a Boolean value using data member values and returns it.
A non-const-predicate method (C++ only) is a predicate method that is not constant.
Examples for C++:
// @stereotype predicate
bool RootBranch::isMultiDimensional() const {
return !(m_number_elements == 1 );
}
// @stereotype predicate
bool RepBase::isSelected() const {
return m_desel == false;
}
// @stereotype non-const-predicate
bool QtFileDialog::isDocSuffix(const std::string & suffix ) {
return suffix == s_doc_suffix || suffix == ".xml";
}
Examples for C#:
// @stereotype predicate
internal bool IsPassThruObjectNeeded()
{
return (_passThru) && (!this.DidUserSuppressTheOperation) && (!this.JobHadErrors);
}
// @stereotype predicate
internal bool IsInsideCatchBlock(int index)
{
return index >= HandlerStartIndex && index < HandlerEndIndex;
}
// @stereotype predicate
internal bool HostInNestedPrompt()
{
if (NestedPromptCount > 0)
{
return true;
}
else
{
return false;
}
}
Examples for Java:
// @stereotype predicate
public boolean isCancelled() {
return get() == CANCELLED;
}
// @stereotype predicate
public final boolean isEmpty() {
return get() != FUSED_READY;
}
// @stereotype predicate
public final boolean isDisposed() {
return get() == DISPOSED;
}
A property is a method that returns information (not Boolean) about an object using data member values.
A non-const-property method (C++ only) is a property method that is not constant.
// @stereotype property
inline double calcRed(double value) const {
if(m_parms[SIGMA] == 0.0) {
Erfc* p = const_cast<Erfc*>(this);
p->m_parms[SIGMA] = 0.0001;
}
return (value - m_parms[MEAN])/m_parms[SIGMA];
}
// @stereotype property
double Range::fraction(double value) const {
return (value - m_min)/(m_max - m_min);
}
// @stereotype non-const-property
size_t count() {
return count_ + this->size();
}
Examples for C#:
// @stereotype property
internal int ColumnFromOffset(int offset)
{
return offset - _lineStartMap[LineFromOffset(offset) - 1] + 1;
}
// @stereotype property
public override int Peek()
{
if (_charCount == 0)
{
if (RefillCharBuffer() == -1)
{
return -1;
}
}
return (int)_charBuff[_charCount - 1];
}
Examples for Java:
// @stereotype property
static int mix(int x) {
final int h = x * INT_PHI;
return h ^ (h >>> 16);
}
// @stereotype property
int calcElementOffset(long index) {
return (int)index & mask;
}
An accessor is a method that returns information about an object through a parameter. This covers the more general case where the method could return information about the attributes using the parameters and the method return.
A non-const-accessor method (C++ only) is a accessor method that is not constant.
Examples for C++:
// @stereotype accessor
virtual void next_bytes(uint8_t* buffer, size_t buffer_size) const {
if (buffer == nullptr) throw argument_null_exception(current_stack_frame_);
for (size_t index = 0; index < buffer_size; index++)
buffer[index] = next<uint8_t>(0, std::numeric_limits<uint8_t>::max());
}
// @stereotype accessor
void DoGetClientSize(int* width, int* height) const override {
wxStaticBox::DoGetSize(width, height);
*width = *width - GetClientAreaOrigin().x - inner_margin;
*height = *height - GetClientAreaOrigin().y - inner_margin;
}
// @stereotype non-const-accessor
inline void visible_cells(int& r1, int& r2, int& c1, int& c2) {
r1 = toprow;
r2 = botrow;
c1 = leftcol;
c2 = rightcol;
}
Examples for C#:
// @stereotype accessor
internal bool IsFuzzyMatch(string candidate, string pattern, out int score)
{
score = GetDamerauLevenshteinDistance(candidate, pattern);
return score <= MinimumDistance;
}
// @stereotype accessor
public static string GetMissingEventMessage(out int parameterCount)
{
parameterCount = 1;
return MissingEventIdResourceName;
}
Examples for Java:
// @stereotype accessor
void dequantize(int[] dataUnit, int iComp) {
int[] qTable = quantizationTables[frameComponents[componentIds[iComp]][TQI]];
for (int i = 0; i < dataUnit.length; i++) {
int zzIndex = ZigZag8x8[i];
dataUnit[zzIndex] = dataUnit[zzIndex] * qTable[i];
}
}
A structural mutator is a method that modifies the state of an object. There are three types: set, command, and non-void-command.
A set method is a mutator that modifies the value of a data member.
Examples for C++;
// @stereotype set
void dataSource::setName(const string& name){
m_ds_name = name;
}
// @stereotype set
void ListTuple::setShape(std::vector<unsigned int>& shape) {
m_shape = shape;
}
Examples for C#:
// @stereotype set
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
_disposed = true;
}
internal TypeTable TypeTable
{
// @stereotype set
set { _serializer.TypeTable = value; }
}
Examples for Java:
// @stereotype set
public void setDone()
{
this.done = true;
}
// @stereotype set
public void accept(Integer t)
{
items++;
}
A command method is a mutator that modifies the state of an object in a complex way.
Examples for C++:
// @stereotype command
void Cursor::_RedrawCursor() noexcept {
if (IsOn() && !IsConversionArea()) {
if (_fDeferCursorRedraw) {
_fHaveDeferredCursorRedraw = true;
}
else {
_RedrawCursorAlways();
}
}
}
// @stereotype non-void-command
basic_memory_buffer& operator=(basic_memory_buffer&& other) FMT_NOEXCEPT {
FMT_ASSERT(this != &other, "");
deallocate();
move(other);
return *this;
}
Examples for C#:
// @stereotype command
private void WriteNameAttribute(string value)
{
Dbg.Assert(!string.IsNullOrEmpty(value), "Caller should validate the parameter");
WriteAttribute(
SerializationStrings.NameAttribute,
EncodeString(value));
}
// @stereotype non-void-command
private static ulong ConvertToUlong(double val)
{
if (val < 0)
{
long lValue = LanguagePrimitives.ConvertTo<long>(val);
return unchecked((ulong)lValue);
}
return LanguagePrimitives.ConvertTo<ulong>(val);
}
Examples for Java:
// @stereotype command
public final void subscribe(@NonNull CompletableObserver observer) {
Objects.requireNonNull(observer, "observer is null");
try {
observer = RxJavaPlugins.onSubscribe(this, observer);
Objects.requireNonNull(observer, "The RxJavaPlugins.onSubscribe hook returned a null CompletableObserver. Please check the handler provided to RxJavaPlugins.setOnCompletableSubscribe for invalid null returns. Further reading: https://github.com/ReactiveX/RxJava/wiki/Plugins");
subscribeActual(observer);
} catch (NullPointerException ex) {
throw ex;
} catch (Throwable ex) {
Exceptions.throwIfFatal(ex);
RxJavaPlugins.onError(ex);
throw toNpe(ex);
}
}
// @stereotype non-void-command
public static <@NonNull T> Observable<T> mergeDelayError(
@NonNull ObservableSource<? extends T> source1, @NonNull ObservableSource<? extends T> source2) {
Objects.requireNonNull(source1, "source1 is null");
Objects.requireNonNull(source2, "source2 is null");
return fromArray(source1, source2).flatMap((Function)Functions.identity(), true, 2);
}
A factory method is a creational method that creates an object and returns it to the client.
Examples for C++:
// @stereotype factory
CutRangeRep* CutRangeRep::clone() {
return new CutRangeRep(*this);
}
// @stereotype factory
CompositePlotter* CompositePlotter::clone () {
return new CompositePlotter(*this);
}
Examples for C#:
// @stereotype factory
protected CimSessionProxy CreateCimSessionProxy(CimSessionProxy originalProxy, bool passThru)
{
CimSessionProxy proxy = new CimSessionProxySetCimInstance(originalProxy, passThru);
this.SubscribeEventAndAddProxytoCache(proxy);
return proxy;
}
// @stereotype factory
internal static PSTraceSource GetNewTraceSource(string name, string description, bool traceHeaders)
{
ArgumentException.ThrowIfNullOrEmpty(name);
string fullName = name;
PSTraceSource result = new PSTraceSource(fullName, name, description, traceHeaders);s
return result;
}
Examples for Java:
// @stereotype factory
public final Completable cache() {
return RxJavaPlugins.onAssembly(new CompletableCache(this));
}
// @stereotype factory
public static <T> UnicastSubject<T> create(boolean delayError) {
return new UnicastSubject<>(bufferSize(), null, delayError);
}
Collaborational methods deal with objects of other classes. There are two types: collaborator, and controller.
A collaborator method works on objects that belong to classes other than itself. These objects are usually passed as parameters, created as local variables, or accessed indirectly through a pointer/reference from some data member.
Examples for C++:
// @stereotype collaborator
template<typename T>
void copy_to(T* other) const
{
other->group(group_);
other->required(required_);
other->ignore_case(ignore_case_);
other->ignore_underscore(ignore_underscore_);
other->configurable(configurable_);
other->disable_flag_override(disable_flag_override_);
other->delimiter(delimiter_);
other->always_capture_default(always_capture_default_);
other->multi_option_policy(multi_option_policy_);
}
// @stereotype collaborator
inline std::string Formatter::make_positionals(const App* app) const
{
std::vector<const Option*> opts =
app->get_options([](const Option* opt) { return !opt->get_group().empty() && opt->get_positional(); });
if (opts.empty())
return std::string();
return make_group(get_label("Positionals"), true, opts);
}
Examples for C#:
// @stereotype collaborator
private void OnNotifyPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
// @stereotype collaborator
protected virtual void NotifyFilterExpressionChanged()
{
EventHandler eh = this.FilterExpressionChanged;
if (eh != null)
{
eh(this, new EventArgs());
}
}
Examples for Java:
// @stereotype collaborator
public void printStackTrace(PrintStream s) {
printStackTrace(new WrappedPrintStream(s));
}
A controller method works only on objects that belong to classes other than itself.
Examples for C++:
// @stereotype controller
inline std::string Formatter::make_option_name(const Option* opt, bool is_positional) const
{
if (is_positional)
return opt->get_name(true, false);
return opt->get_name(false, true);
}
// @stereotype controller
bool operator()(const Font& lhs, const Font& rhs) const
{
return lhs.LocalizedName() < rhs.LocalizedName();
}
Examples for C#:
// @stereotype controller
private void ViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "Zoom")
{
HelpWindowSettings.Default.HelpZoom = this.viewModel.Zoom;
}
}
Examples for Java:
// @stereotype controller
public void flowFlatMapIterable1(Blackhole bh) {
flowFlatMapIterable1.subscribe(new PerfConsumer(bh));
}
A degenerate method is a method that has no use of the object's state. There are three types: stateless, wrapper, and empty[8]
A stateless method is a method that does not read or change an object’s state directly nor indirectly and does not make calls to other methods of the same class.
Examples for C++:
// @stereotype stateless
point add(const point& n1, const point& n2) {
return n1 + n2;
}
// @stereotype stateless
bool NTupleChiSqFCN::needsIntegrated() const {
return false;
}
Examples for C#:
// @stereotype stateless
protected override string GetSourceObjectEventName()
{
return "CimIndicationArrived";
}
// @stereotype stateless
private static string GetPipePath(string pipeName)
{
// Platform is a static class
if (Platform.IsWindows)
{
return $@"\\.\pipe\{pipeName}";
}
// Path is a static class
return $@"{Path.GetTempPath()}CoreFxPipe_{pipeName}";
}
Examples for Java:
// @stereotype stateless
public long maxElementsFromPublisher() {
return 1;
}
// @stereotype stateless
public void cleanup() {
// Scheduler is a static field defined in a different class that is not inherited
Scheduler.IS_DRIFT_USE_NANOTIME = false;
}
A wrapper method is a method that does not read or change an object’s state directly. This method can have one call of any kind (method call, function call, or a constructor call)
Examples for C++:
// @stereotype wrapper
virtual double errorDef () const {
return up();
}
// @stereotype wrapper
bool _isConnected() const noexcept {
// ConnectionState is an enum
return _isStateOneOf(ConnectionState::Connected);
}
Examples for C#:
// @stereotype wrapper
public override void EnterNestedPrompt()
{
EnterNestedPrompt(null);
}
// @stereotype wrapper
public override void WriteDebugLine(string message)
{
// message is not a field
WriteDebugLineHelper(message);
}
Examples for Java:
// @stereotype wrapper
public void simpleOneLessAsyncLoop() {
for (int i = 0; i < 200; i++) {
simpleOneLessAsync();
}
}
// @stereotype wrapper
public void onComplete() {
countDown();
}
An empty is a method that has no statements (except for comments).
Examples for C++:
// @stereotype empty
void hippodraw::Observer::willDelete(const Observable*) {
// do nothing, default behaviour
}
Examples for C#:
// @stereotype empty
internal virtual void RemoveCommandTransportManager(Guid powerShellCmdId)
{
}
Examples for Java:
// @stereotype empty
public void onSubscribe(Disposable d)
{
}
A method can be labeled with one or more stereotypes from each category.
Examples for C++:
// @stereotype non-void-command factory collaborator
plotterBase* displayController::createDisplay(const string& name){
dataRepController* controller = dataRepController::instance();
dataRep* rep = controller->createDataRep(name);
return createDisplay(rep);
}
Examples for C#:
// @stereotype non-void-command collaborator
public static int Main(string[] args)
{
using (PowerShell ps = PowerShell.Create())
{
Console.WriteLine("\nEvaluating 'Get-Command Write-Output' in PS Core Runspace\n");
var results = ps.AddScript("Get-Command Write-Output").Invoke();
Console.WriteLine(results[0].ToString());
ps.Commands.Clear();
Console.WriteLine("\nEvaluating '([S.M.A.ActionPreference], [S.M.A.AliasAttribute]).FullName' in PS Core Runspace\n");
results = ps.AddScript("([System.Management.Automation.ActionPreference], [System.Management.Automation.AliasAttribute]).FullName").Invoke();
foreach (dynamic result in results)
{
Console.WriteLine(result.ToString());
}
}
return 0;
}
Examples for Java:
// @stereotype factory controller wrapper
public static <T> Predicate<T> equalsWith(T value) {
return new EqualsPredicate<>(value);
}
[1] Dragan, N., Collard, M. L., & Maletic, J. I. (2006, September). Reverse engineering method stereotypes. In 2006 22nd IEEE International Conference on Software Maintenance (pp. 24-34). IEEE.
[2] Guarnera, D., Collard, M. L., Dragan, N., Maletic, J. I., Newman, C., & Decker, M. (2018, September). Automatically Redocumenting Source Code with Method and Class Stereotypes. In 2018 IEEE Third International Workshop on Dynamic Software Documentation (DySDoc3) (pp. 3-4). IEEE.
[3] Decker, M. J., Newman, C. D., Dragan, N., Collard, M. L., Maletic, J. I., & Kraft, N. A. (2018, May). A taxonomy of how method stereotypes change. In Proceedings of the 40th International Conference on Software Engineering: Companion Proceeedings (pp. 337-338).
[4] Alhindawi, N., Dragan, N., Collard, M. L., & Maletic, J. I. (2013, September). Improving feature location by enhancing source code with stereotypes. In 2013 IEEE International Conference on Software Maintenance (pp. 300-309). Ieee.
[5] Dragan, N., Collard, M. L., & Maletic, J. I. (2010, September). Automatic identification of class stereotypes. In 2010 IEEE International Conference on Software Maintenance (pp. 1-10). IEEE.
[6] Abid, N. J., Dragan, N., Collard, M. L., & Maletic, J. I. (2015, September). Using stereotypes in the automatic generation of natural language summaries for c++ methods. In 2015 IEEE International Conference on Software Maintenance and Evolution (ICSME) (pp. 561-565). IEEE.
[7] Dragan, N., Collard, M. L., Hammad, M., & Maletic, J. I. (2011, September). Using stereotypes to help characterize commits. In 2011 27th IEEE International Conference on Software Maintenance (ICSM) (pp. 520-523). IEEE.
[8] Dragan, N., Collard, M. L., & Maletic, J. I. (2009, September). Using method stereotype distribution as a signature descriptor for software systems. In 2009 IEEE International Conference on Software Maintenance (pp. 567-570). IEEE.